001 /*
002 * Copyright 2007-2016 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2016 UnboundID Corp.
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021 package com.unboundid.ldap.sdk;
022
023
024
025 import java.math.BigInteger;
026 import java.util.ArrayList;
027 import java.util.Arrays;
028 import java.util.Collection;
029 import java.util.Collections;
030 import java.util.Date;
031 import java.util.HashSet;
032 import java.util.Iterator;
033 import java.util.LinkedHashMap;
034 import java.util.List;
035 import java.util.Map;
036 import java.util.Set;
037 import java.util.StringTokenizer;
038
039 import com.unboundid.asn1.ASN1OctetString;
040 import com.unboundid.ldap.matchingrules.MatchingRule;
041 import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition;
042 import com.unboundid.ldap.sdk.schema.Schema;
043 import com.unboundid.ldif.LDIFException;
044 import com.unboundid.ldif.LDIFReader;
045 import com.unboundid.ldif.LDIFRecord;
046 import com.unboundid.ldif.LDIFWriter;
047 import com.unboundid.util.ByteStringBuffer;
048
049 import static com.unboundid.ldap.sdk.LDAPMessages.*;
050 import static com.unboundid.util.Debug.*;
051 import static com.unboundid.util.StaticUtils.*;
052 import static com.unboundid.util.Validator.*;
053
054
055
056 /**
057 * This class provides a data structure for holding information about an LDAP
058 * entry. An entry contains a distinguished name (DN) and a set of attributes.
059 * An entry can be created from these components, and it can also be created
060 * from its LDIF representation as described in
061 * <A HREF="http://www.ietf.org/rfc/rfc2849.txt">RFC 2849</A>. For example:
062 * <BR><BR>
063 * <PRE>
064 * Entry entry = new Entry(
065 * "dn: dc=example,dc=com",
066 * "objectClass: top",
067 * "objectClass: domain",
068 * "dc: example");
069 * </PRE>
070 * <BR><BR>
071 * This class also provides methods for retrieving the LDIF representation of
072 * an entry, either as a single string or as an array of strings that make up
073 * the LDIF lines.
074 * <BR><BR>
075 * The {@code Entry#diff} method may be used to obtain the set of differences
076 * between two entries, and to retrieve a list of {@code Modification} objects
077 * that can be used to modify one entry so that it contains the same set of
078 * data as another. The {@code Entry#applyModifications} method may be used to
079 * apply a set of modifications to an entry.
080 * <BR><BR>
081 * Entry objects are mutable, and the DN, set of attributes, and individual
082 * attribute values can be altered.
083 */
084 public class Entry
085 implements LDIFRecord
086 {
087 /**
088 * The serial version UID for this serializable class.
089 */
090 private static final long serialVersionUID = -4438809025903729197L;
091
092
093
094 // The parsed DN for this entry.
095 private volatile DN parsedDN;
096
097 // The set of attributes for this entry.
098 private final LinkedHashMap<String,Attribute> attributes;
099
100 // The schema to use for this entry.
101 private final Schema schema;
102
103 // The DN for this entry.
104 private String dn;
105
106
107
108 /**
109 * Creates a new entry that wraps the provided entry.
110 *
111 * @param e The entry to be wrapped.
112 */
113 protected Entry(final Entry e)
114 {
115 parsedDN = e.parsedDN;
116 attributes = e.attributes;
117 schema = e.schema;
118 dn = e.dn;
119 }
120
121
122
123 /**
124 * Creates a new entry with the provided DN and no attributes.
125 *
126 * @param dn The DN for this entry. It must not be {@code null}.
127 */
128 public Entry(final String dn)
129 {
130 this(dn, (Schema) null);
131 }
132
133
134
135 /**
136 * Creates a new entry with the provided DN and no attributes.
137 *
138 * @param dn The DN for this entry. It must not be {@code null}.
139 * @param schema The schema to use for operations involving this entry. It
140 * may be {@code null} if no schema is available.
141 */
142 public Entry(final String dn, final Schema schema)
143 {
144 ensureNotNull(dn);
145
146 this.dn = dn;
147 this.schema = schema;
148
149 attributes = new LinkedHashMap<String,Attribute>();
150 }
151
152
153
154 /**
155 * Creates a new entry with the provided DN and no attributes.
156 *
157 * @param dn The DN for this entry. It must not be {@code null}.
158 */
159 public Entry(final DN dn)
160 {
161 this(dn, (Schema) null);
162 }
163
164
165
166 /**
167 * Creates a new entry with the provided DN and no attributes.
168 *
169 * @param dn The DN for this entry. It must not be {@code null}.
170 * @param schema The schema to use for operations involving this entry. It
171 * may be {@code null} if no schema is available.
172 */
173 public Entry(final DN dn, final Schema schema)
174 {
175 ensureNotNull(dn);
176
177 parsedDN = dn;
178 this.dn = parsedDN.toString();
179 this.schema = schema;
180
181 attributes = new LinkedHashMap<String,Attribute>();
182 }
183
184
185
186 /**
187 * Creates a new entry with the provided DN and set of attributes.
188 *
189 * @param dn The DN for this entry. It must not be {@code null}.
190 * @param attributes The set of attributes for this entry. It must not be
191 * {@code null}.
192 */
193 public Entry(final String dn, final Attribute... attributes)
194 {
195 this(dn, null, attributes);
196 }
197
198
199
200 /**
201 * Creates a new entry with the provided DN and set of attributes.
202 *
203 * @param dn The DN for this entry. It must not be {@code null}.
204 * @param schema The schema to use for operations involving this entry.
205 * It may be {@code null} if no schema is available.
206 * @param attributes The set of attributes for this entry. It must not be
207 * {@code null}.
208 */
209 public Entry(final String dn, final Schema schema,
210 final Attribute... attributes)
211 {
212 ensureNotNull(dn, attributes);
213
214 this.dn = dn;
215 this.schema = schema;
216
217 this.attributes = new LinkedHashMap<String,Attribute>(attributes.length);
218 for (final Attribute a : attributes)
219 {
220 final String name = toLowerCase(a.getName());
221 final Attribute attr = this.attributes.get(name);
222 if (attr == null)
223 {
224 this.attributes.put(name, a);
225 }
226 else
227 {
228 this.attributes.put(name, Attribute.mergeAttributes(attr, a));
229 }
230 }
231 }
232
233
234
235 /**
236 * Creates a new entry with the provided DN and set of attributes.
237 *
238 * @param dn The DN for this entry. It must not be {@code null}.
239 * @param attributes The set of attributes for this entry. It must not be
240 * {@code null}.
241 */
242 public Entry(final DN dn, final Attribute... attributes)
243 {
244 this(dn, null, attributes);
245 }
246
247
248
249 /**
250 * Creates a new entry with the provided DN and set of attributes.
251 *
252 * @param dn The DN for this entry. It must not be {@code null}.
253 * @param schema The schema to use for operations involving this entry.
254 * It may be {@code null} if no schema is available.
255 * @param attributes The set of attributes for this entry. It must not be
256 * {@code null}.
257 */
258 public Entry(final DN dn, final Schema schema, final Attribute... attributes)
259 {
260 ensureNotNull(dn, attributes);
261
262 parsedDN = dn;
263 this.dn = parsedDN.toString();
264 this.schema = schema;
265
266 this.attributes = new LinkedHashMap<String,Attribute>(attributes.length);
267 for (final Attribute a : attributes)
268 {
269 final String name = toLowerCase(a.getName());
270 final Attribute attr = this.attributes.get(name);
271 if (attr == null)
272 {
273 this.attributes.put(name, a);
274 }
275 else
276 {
277 this.attributes.put(name, Attribute.mergeAttributes(attr, a));
278 }
279 }
280 }
281
282
283
284 /**
285 * Creates a new entry with the provided DN and set of attributes.
286 *
287 * @param dn The DN for this entry. It must not be {@code null}.
288 * @param attributes The set of attributes for this entry. It must not be
289 * {@code null}.
290 */
291 public Entry(final String dn, final Collection<Attribute> attributes)
292 {
293 this(dn, null, attributes);
294 }
295
296
297
298 /**
299 * Creates a new entry with the provided DN and set of attributes.
300 *
301 * @param dn The DN for this entry. It must not be {@code null}.
302 * @param schema The schema to use for operations involving this entry.
303 * It may be {@code null} if no schema is available.
304 * @param attributes The set of attributes for this entry. It must not be
305 * {@code null}.
306 */
307 public Entry(final String dn, final Schema schema,
308 final Collection<Attribute> attributes)
309 {
310 ensureNotNull(dn, attributes);
311
312 this.dn = dn;
313 this.schema = schema;
314
315 this.attributes = new LinkedHashMap<String,Attribute>(attributes.size());
316 for (final Attribute a : attributes)
317 {
318 final String name = toLowerCase(a.getName());
319 final Attribute attr = this.attributes.get(name);
320 if (attr == null)
321 {
322 this.attributes.put(name, a);
323 }
324 else
325 {
326 this.attributes.put(name, Attribute.mergeAttributes(attr, a));
327 }
328 }
329 }
330
331
332
333 /**
334 * Creates a new entry with the provided DN and set of attributes.
335 *
336 * @param dn The DN for this entry. It must not be {@code null}.
337 * @param attributes The set of attributes for this entry. It must not be
338 * {@code null}.
339 */
340 public Entry(final DN dn, final Collection<Attribute> attributes)
341 {
342 this(dn, null, attributes);
343 }
344
345
346
347 /**
348 * Creates a new entry with the provided DN and set of attributes.
349 *
350 * @param dn The DN for this entry. It must not be {@code null}.
351 * @param schema The schema to use for operations involving this entry.
352 * It may be {@code null} if no schema is available.
353 * @param attributes The set of attributes for this entry. It must not be
354 * {@code null}.
355 */
356 public Entry(final DN dn, final Schema schema,
357 final Collection<Attribute> attributes)
358 {
359 ensureNotNull(dn, attributes);
360
361 parsedDN = dn;
362 this.dn = parsedDN.toString();
363 this.schema = schema;
364
365 this.attributes = new LinkedHashMap<String,Attribute>(attributes.size());
366 for (final Attribute a : attributes)
367 {
368 final String name = toLowerCase(a.getName());
369 final Attribute attr = this.attributes.get(name);
370 if (attr == null)
371 {
372 this.attributes.put(name, a);
373 }
374 else
375 {
376 this.attributes.put(name, Attribute.mergeAttributes(attr, a));
377 }
378 }
379 }
380
381
382
383 /**
384 * Creates a new entry from the provided LDIF representation.
385 *
386 * @param entryLines The set of lines that comprise an LDIF representation
387 * of the entry. It must not be {@code null} or empty.
388 *
389 * @throws LDIFException If the provided lines cannot be decoded as an entry
390 * in LDIF format.
391 */
392 public Entry(final String... entryLines)
393 throws LDIFException
394 {
395 this(null, entryLines);
396 }
397
398
399
400 /**
401 * Creates a new entry from the provided LDIF representation.
402 *
403 * @param schema The schema to use for operations involving this entry.
404 * It may be {@code null} if no schema is available.
405 * @param entryLines The set of lines that comprise an LDIF representation
406 * of the entry. It must not be {@code null} or empty.
407 *
408 * @throws LDIFException If the provided lines cannot be decoded as an entry
409 * in LDIF format.
410 */
411 public Entry(final Schema schema, final String... entryLines)
412 throws LDIFException
413 {
414 final Entry e = LDIFReader.decodeEntry(false, schema, entryLines);
415
416 this.schema = schema;
417
418 dn = e.dn;
419 parsedDN = e.parsedDN;
420 attributes = e.attributes;
421 }
422
423
424
425 /**
426 * Retrieves the DN for this entry.
427 *
428 * @return The DN for this entry.
429 */
430 public final String getDN()
431 {
432 return dn;
433 }
434
435
436
437 /**
438 * Specifies the DN for this entry.
439 *
440 * @param dn The DN for this entry. It must not be {@code null}.
441 */
442 public void setDN(final String dn)
443 {
444 ensureNotNull(dn);
445
446 this.dn = dn;
447 parsedDN = null;
448 }
449
450
451
452 /**
453 * Specifies the DN for this entry.
454 *
455 * @param dn The DN for this entry. It must not be {@code null}.
456 */
457 public void setDN(final DN dn)
458 {
459 ensureNotNull(dn);
460
461 parsedDN = dn;
462 this.dn = parsedDN.toString();
463 }
464
465
466
467 /**
468 * Retrieves the parsed DN for this entry.
469 *
470 * @return The parsed DN for this entry.
471 *
472 * @throws LDAPException If the DN string cannot be parsed as a valid DN.
473 */
474 public final DN getParsedDN()
475 throws LDAPException
476 {
477 if (parsedDN == null)
478 {
479 parsedDN = new DN(dn, schema);
480 }
481
482 return parsedDN;
483 }
484
485
486
487 /**
488 * Retrieves the RDN for this entry.
489 *
490 * @return The RDN for this entry, or {@code null} if the DN is the null DN.
491 *
492 * @throws LDAPException If the DN string cannot be parsed as a valid DN.
493 */
494 public final RDN getRDN()
495 throws LDAPException
496 {
497 return getParsedDN().getRDN();
498 }
499
500
501
502 /**
503 * Retrieves the parent DN for this entry.
504 *
505 * @return The parent DN for this entry, or {@code null} if there is no
506 * parent.
507 *
508 * @throws LDAPException If the DN string cannot be parsed as a valid DN.
509 */
510 public final DN getParentDN()
511 throws LDAPException
512 {
513 if (parsedDN == null)
514 {
515 parsedDN = new DN(dn, schema);
516 }
517
518 return parsedDN.getParent();
519 }
520
521
522
523 /**
524 * Retrieves the parent DN for this entry as a string.
525 *
526 * @return The parent DN for this entry as a string, or {@code null} if there
527 * is no parent.
528 *
529 * @throws LDAPException If the DN string cannot be parsed as a valid DN.
530 */
531 public final String getParentDNString()
532 throws LDAPException
533 {
534 if (parsedDN == null)
535 {
536 parsedDN = new DN(dn, schema);
537 }
538
539 final DN parentDN = parsedDN.getParent();
540 if (parentDN == null)
541 {
542 return null;
543 }
544 else
545 {
546 return parentDN.toString();
547 }
548 }
549
550
551
552 /**
553 * Retrieves the schema that will be used for this entry, if any.
554 *
555 * @return The schema that will be used for this entry, or {@code null} if
556 * no schema was provided.
557 */
558 protected Schema getSchema()
559 {
560 return schema;
561 }
562
563
564
565 /**
566 * Indicates whether this entry contains the specified attribute.
567 *
568 * @param attributeName The name of the attribute for which to make the
569 * determination. It must not be {@code null}.
570 *
571 * @return {@code true} if this entry contains the specified attribute, or
572 * {@code false} if not.
573 */
574 public final boolean hasAttribute(final String attributeName)
575 {
576 return hasAttribute(attributeName, schema);
577 }
578
579
580
581 /**
582 * Indicates whether this entry contains the specified attribute.
583 *
584 * @param attributeName The name of the attribute for which to make the
585 * determination. It must not be {@code null}.
586 * @param schema The schema to use to determine whether there may be
587 * alternate names for the specified attribute. It may
588 * be {@code null} if no schema is available.
589 *
590 * @return {@code true} if this entry contains the specified attribute, or
591 * {@code false} if not.
592 */
593 public final boolean hasAttribute(final String attributeName,
594 final Schema schema)
595 {
596 ensureNotNull(attributeName);
597
598 if (attributes.containsKey(toLowerCase(attributeName)))
599 {
600 return true;
601 }
602
603 if (schema != null)
604 {
605 final String baseName;
606 final String options;
607 final int semicolonPos = attributeName.indexOf(';');
608 if (semicolonPos > 0)
609 {
610 baseName = attributeName.substring(0, semicolonPos);
611 options = toLowerCase(attributeName.substring(semicolonPos));
612 }
613 else
614 {
615 baseName = attributeName;
616 options = "";
617 }
618
619 final AttributeTypeDefinition at = schema.getAttributeType(baseName);
620 if (at != null)
621 {
622 if (attributes.containsKey(toLowerCase(at.getOID()) + options))
623 {
624 return true;
625 }
626
627 for (final String name : at.getNames())
628 {
629 if (attributes.containsKey(toLowerCase(name) + options))
630 {
631 return true;
632 }
633 }
634 }
635 }
636
637 return false;
638 }
639
640
641
642 /**
643 * Indicates whether this entry contains the specified attribute. It will
644 * only return {@code true} if this entry contains an attribute with the same
645 * name and exact set of values.
646 *
647 * @param attribute The attribute for which to make the determination. It
648 * must not be {@code null}.
649 *
650 * @return {@code true} if this entry contains the specified attribute, or
651 * {@code false} if not.
652 */
653 public final boolean hasAttribute(final Attribute attribute)
654 {
655 ensureNotNull(attribute);
656
657 final String lowerName = toLowerCase(attribute.getName());
658 final Attribute attr = attributes.get(lowerName);
659 return ((attr != null) && attr.equals(attribute));
660 }
661
662
663
664 /**
665 * Indicates whether this entry contains an attribute with the given name and
666 * value.
667 *
668 * @param attributeName The name of the attribute for which to make the
669 * determination. It must not be {@code null}.
670 * @param attributeValue The value for which to make the determination. It
671 * must not be {@code null}.
672 *
673 * @return {@code true} if this entry contains an attribute with the
674 * specified name and value, or {@code false} if not.
675 */
676 public final boolean hasAttributeValue(final String attributeName,
677 final String attributeValue)
678 {
679 ensureNotNull(attributeName, attributeValue);
680
681 final Attribute attr = attributes.get(toLowerCase(attributeName));
682 return ((attr != null) && attr.hasValue(attributeValue));
683 }
684
685
686
687 /**
688 * Indicates whether this entry contains an attribute with the given name and
689 * value.
690 *
691 * @param attributeName The name of the attribute for which to make the
692 * determination. It must not be {@code null}.
693 * @param attributeValue The value for which to make the determination. It
694 * must not be {@code null}.
695 * @param matchingRule The matching rule to use to make the determination.
696 * It must not be {@code null}.
697 *
698 * @return {@code true} if this entry contains an attribute with the
699 * specified name and value, or {@code false} if not.
700 */
701 public final boolean hasAttributeValue(final String attributeName,
702 final String attributeValue,
703 final MatchingRule matchingRule)
704 {
705 ensureNotNull(attributeName, attributeValue);
706
707 final Attribute attr = attributes.get(toLowerCase(attributeName));
708 return ((attr != null) && attr.hasValue(attributeValue, matchingRule));
709 }
710
711
712
713 /**
714 * Indicates whether this entry contains an attribute with the given name and
715 * value.
716 *
717 * @param attributeName The name of the attribute for which to make the
718 * determination. It must not be {@code null}.
719 * @param attributeValue The value for which to make the determination. It
720 * must not be {@code null}.
721 *
722 * @return {@code true} if this entry contains an attribute with the
723 * specified name and value, or {@code false} if not.
724 */
725 public final boolean hasAttributeValue(final String attributeName,
726 final byte[] attributeValue)
727 {
728 ensureNotNull(attributeName, attributeValue);
729
730 final Attribute attr = attributes.get(toLowerCase(attributeName));
731 return ((attr != null) && attr.hasValue(attributeValue));
732 }
733
734
735
736 /**
737 * Indicates whether this entry contains an attribute with the given name and
738 * value.
739 *
740 * @param attributeName The name of the attribute for which to make the
741 * determination. It must not be {@code null}.
742 * @param attributeValue The value for which to make the determination. It
743 * must not be {@code null}.
744 * @param matchingRule The matching rule to use to make the determination.
745 * It must not be {@code null}.
746 *
747 * @return {@code true} if this entry contains an attribute with the
748 * specified name and value, or {@code false} if not.
749 */
750 public final boolean hasAttributeValue(final String attributeName,
751 final byte[] attributeValue,
752 final MatchingRule matchingRule)
753 {
754 ensureNotNull(attributeName, attributeValue);
755
756 final Attribute attr = attributes.get(toLowerCase(attributeName));
757 return ((attr != null) && attr.hasValue(attributeValue, matchingRule));
758 }
759
760
761
762 /**
763 * Indicates whether this entry contains the specified object class.
764 *
765 * @param objectClassName The name of the object class for which to make the
766 * determination. It must not be {@code null}.
767 *
768 * @return {@code true} if this entry contains the specified object class, or
769 * {@code false} if not.
770 */
771 public final boolean hasObjectClass(final String objectClassName)
772 {
773 return hasAttributeValue("objectClass", objectClassName);
774 }
775
776
777
778 /**
779 * Retrieves the set of attributes contained in this entry.
780 *
781 * @return The set of attributes contained in this entry.
782 */
783 public final Collection<Attribute> getAttributes()
784 {
785 return Collections.unmodifiableCollection(attributes.values());
786 }
787
788
789
790 /**
791 * Retrieves the attribute with the specified name.
792 *
793 * @param attributeName The name of the attribute to retrieve. It must not
794 * be {@code null}.
795 *
796 * @return The requested attribute from this entry, or {@code null} if the
797 * specified attribute is not present in this entry.
798 */
799 public final Attribute getAttribute(final String attributeName)
800 {
801 return getAttribute(attributeName, schema);
802 }
803
804
805
806 /**
807 * Retrieves the attribute with the specified name.
808 *
809 * @param attributeName The name of the attribute to retrieve. It must not
810 * be {@code null}.
811 * @param schema The schema to use to determine whether there may be
812 * alternate names for the specified attribute. It may
813 * be {@code null} if no schema is available.
814 *
815 * @return The requested attribute from this entry, or {@code null} if the
816 * specified attribute is not present in this entry.
817 */
818 public final Attribute getAttribute(final String attributeName,
819 final Schema schema)
820 {
821 ensureNotNull(attributeName);
822
823 Attribute a = attributes.get(toLowerCase(attributeName));
824 if ((a == null) && (schema != null))
825 {
826 final String baseName;
827 final String options;
828 final int semicolonPos = attributeName.indexOf(';');
829 if (semicolonPos > 0)
830 {
831 baseName = attributeName.substring(0, semicolonPos);
832 options = toLowerCase(attributeName.substring(semicolonPos));
833 }
834 else
835 {
836 baseName = attributeName;
837 options = "";
838 }
839
840 final AttributeTypeDefinition at = schema.getAttributeType(baseName);
841 if (at == null)
842 {
843 return null;
844 }
845
846 a = attributes.get(toLowerCase(at.getOID() + options));
847 if (a == null)
848 {
849 for (final String name : at.getNames())
850 {
851 a = attributes.get(toLowerCase(name) + options);
852 if (a != null)
853 {
854 return a;
855 }
856 }
857 }
858
859 return a;
860 }
861 else
862 {
863 return a;
864 }
865 }
866
867
868
869 /**
870 * Retrieves the list of attributes with the given base name and all of the
871 * specified options.
872 *
873 * @param baseName The base name (without any options) for the attribute to
874 * retrieve. It must not be {@code null}.
875 * @param options The set of options that should be included in the
876 * attributes that are returned. It may be empty or
877 * {@code null} if all attributes with the specified base
878 * name should be returned, regardless of the options that
879 * they contain (if any).
880 *
881 * @return The list of attributes with the given base name and all of the
882 * specified options. It may be empty if there are no attributes
883 * with the specified base name and set of options.
884 */
885 public final List<Attribute> getAttributesWithOptions(final String baseName,
886 final Set<String> options)
887 {
888 ensureNotNull(baseName);
889
890 final ArrayList<Attribute> attrList = new ArrayList<Attribute>(10);
891
892 for (final Attribute a : attributes.values())
893 {
894 if (a.getBaseName().equalsIgnoreCase(baseName))
895 {
896 if ((options == null) || options.isEmpty())
897 {
898 attrList.add(a);
899 }
900 else
901 {
902 boolean allFound = true;
903 for (final String option : options)
904 {
905 if (! a.hasOption(option))
906 {
907 allFound = false;
908 break;
909 }
910 }
911
912 if (allFound)
913 {
914 attrList.add(a);
915 }
916 }
917 }
918 }
919
920 return Collections.unmodifiableList(attrList);
921 }
922
923
924
925 /**
926 * Retrieves the value for the specified attribute, if available. If the
927 * attribute has more than one value, then the first value will be returned.
928 *
929 * @param attributeName The name of the attribute for which to retrieve the
930 * value. It must not be {@code null}.
931 *
932 * @return The value for the specified attribute, or {@code null} if that
933 * attribute is not available.
934 */
935 public String getAttributeValue(final String attributeName)
936 {
937 ensureNotNull(attributeName);
938
939 final Attribute a = attributes.get(toLowerCase(attributeName));
940 if (a == null)
941 {
942 return null;
943 }
944 else
945 {
946 return a.getValue();
947 }
948 }
949
950
951
952 /**
953 * Retrieves the value for the specified attribute as a byte array, if
954 * available. If the attribute has more than one value, then the first value
955 * will be returned.
956 *
957 * @param attributeName The name of the attribute for which to retrieve the
958 * value. It must not be {@code null}.
959 *
960 * @return The value for the specified attribute as a byte array, or
961 * {@code null} if that attribute is not available.
962 */
963 public byte[] getAttributeValueBytes(final String attributeName)
964 {
965 ensureNotNull(attributeName);
966
967 final Attribute a = attributes.get(toLowerCase(attributeName));
968 if (a == null)
969 {
970 return null;
971 }
972 else
973 {
974 return a.getValueByteArray();
975 }
976 }
977
978
979
980 /**
981 * Retrieves the value for the specified attribute as a Boolean, if available.
982 * If the attribute has more than one value, then the first value will be
983 * returned. Values of "true", "t", "yes", "y", "on", and "1" will be
984 * interpreted as {@code TRUE}. Values of "false", "f", "no", "n", "off", and
985 * "0" will be interpreted as {@code FALSE}.
986 *
987 * @param attributeName The name of the attribute for which to retrieve the
988 * value. It must not be {@code null}.
989 *
990 * @return The Boolean value parsed from the specified attribute, or
991 * {@code null} if that attribute is not available or the value
992 * cannot be parsed as a Boolean.
993 */
994 public Boolean getAttributeValueAsBoolean(final String attributeName)
995 {
996 ensureNotNull(attributeName);
997
998 final Attribute a = attributes.get(toLowerCase(attributeName));
999 if (a == null)
1000 {
1001 return null;
1002 }
1003 else
1004 {
1005 return a.getValueAsBoolean();
1006 }
1007 }
1008
1009
1010
1011 /**
1012 * Retrieves the value for the specified attribute as a Date, formatted using
1013 * the generalized time syntax, if available. If the attribute has more than
1014 * one value, then the first value will be returned.
1015 *
1016 * @param attributeName The name of the attribute for which to retrieve the
1017 * value. It must not be {@code null}.
1018 *
1019 * @return The Date value parsed from the specified attribute, or
1020 * {@code null} if that attribute is not available or the value
1021 * cannot be parsed as a Date.
1022 */
1023 public Date getAttributeValueAsDate(final String attributeName)
1024 {
1025 ensureNotNull(attributeName);
1026
1027 final Attribute a = attributes.get(toLowerCase(attributeName));
1028 if (a == null)
1029 {
1030 return null;
1031 }
1032 else
1033 {
1034 return a.getValueAsDate();
1035 }
1036 }
1037
1038
1039
1040 /**
1041 * Retrieves the value for the specified attribute as a DN, if available. If
1042 * the attribute has more than one value, then the first value will be
1043 * returned.
1044 *
1045 * @param attributeName The name of the attribute for which to retrieve the
1046 * value. It must not be {@code null}.
1047 *
1048 * @return The DN value parsed from the specified attribute, or {@code null}
1049 * if that attribute is not available or the value cannot be parsed
1050 * as a DN.
1051 */
1052 public DN getAttributeValueAsDN(final String attributeName)
1053 {
1054 ensureNotNull(attributeName);
1055
1056 final Attribute a = attributes.get(toLowerCase(attributeName));
1057 if (a == null)
1058 {
1059 return null;
1060 }
1061 else
1062 {
1063 return a.getValueAsDN();
1064 }
1065 }
1066
1067
1068
1069 /**
1070 * Retrieves the value for the specified attribute as an Integer, if
1071 * available. If the attribute has more than one value, then the first value
1072 * will be returned.
1073 *
1074 * @param attributeName The name of the attribute for which to retrieve the
1075 * value. It must not be {@code null}.
1076 *
1077 * @return The Integer value parsed from the specified attribute, or
1078 * {@code null} if that attribute is not available or the value
1079 * cannot be parsed as an Integer.
1080 */
1081 public Integer getAttributeValueAsInteger(final String attributeName)
1082 {
1083 ensureNotNull(attributeName);
1084
1085 final Attribute a = attributes.get(toLowerCase(attributeName));
1086 if (a == null)
1087 {
1088 return null;
1089 }
1090 else
1091 {
1092 return a.getValueAsInteger();
1093 }
1094 }
1095
1096
1097
1098 /**
1099 * Retrieves the value for the specified attribute as a Long, if available.
1100 * If the attribute has more than one value, then the first value will be
1101 * returned.
1102 *
1103 * @param attributeName The name of the attribute for which to retrieve the
1104 * value. It must not be {@code null}.
1105 *
1106 * @return The Long value parsed from the specified attribute, or
1107 * {@code null} if that attribute is not available or the value
1108 * cannot be parsed as a Long.
1109 */
1110 public Long getAttributeValueAsLong(final String attributeName)
1111 {
1112 ensureNotNull(attributeName);
1113
1114 final Attribute a = attributes.get(toLowerCase(attributeName));
1115 if (a == null)
1116 {
1117 return null;
1118 }
1119 else
1120 {
1121 return a.getValueAsLong();
1122 }
1123 }
1124
1125
1126
1127 /**
1128 * Retrieves the set of values for the specified attribute, if available.
1129 *
1130 * @param attributeName The name of the attribute for which to retrieve the
1131 * values. It must not be {@code null}.
1132 *
1133 * @return The set of values for the specified attribute, or {@code null} if
1134 * that attribute is not available.
1135 */
1136 public String[] getAttributeValues(final String attributeName)
1137 {
1138 ensureNotNull(attributeName);
1139
1140 final Attribute a = attributes.get(toLowerCase(attributeName));
1141 if (a == null)
1142 {
1143 return null;
1144 }
1145 else
1146 {
1147 return a.getValues();
1148 }
1149 }
1150
1151
1152
1153 /**
1154 * Retrieves the set of values for the specified attribute as byte arrays, if
1155 * available.
1156 *
1157 * @param attributeName The name of the attribute for which to retrieve the
1158 * values. It must not be {@code null}.
1159 *
1160 * @return The set of values for the specified attribute as byte arrays, or
1161 * {@code null} if that attribute is not available.
1162 */
1163 public byte[][] getAttributeValueByteArrays(final String attributeName)
1164 {
1165 ensureNotNull(attributeName);
1166
1167 final Attribute a = attributes.get(toLowerCase(attributeName));
1168 if (a == null)
1169 {
1170 return null;
1171 }
1172 else
1173 {
1174 return a.getValueByteArrays();
1175 }
1176 }
1177
1178
1179
1180 /**
1181 * Retrieves the "objectClass" attribute from the entry, if available.
1182 *
1183 * @return The "objectClass" attribute from the entry, or {@code null} if
1184 * that attribute not available.
1185 */
1186 public final Attribute getObjectClassAttribute()
1187 {
1188 return getAttribute("objectClass");
1189 }
1190
1191
1192
1193 /**
1194 * Retrieves the values of the "objectClass" attribute from the entry, if
1195 * available.
1196 *
1197 * @return The values of the "objectClass" attribute from the entry, or
1198 * {@code null} if that attribute is not available.
1199 */
1200 public final String[] getObjectClassValues()
1201 {
1202 return getAttributeValues("objectClass");
1203 }
1204
1205
1206
1207 /**
1208 * Adds the provided attribute to this entry. If this entry already contains
1209 * an attribute with the same name, then their values will be merged.
1210 *
1211 * @param attribute The attribute to be added. It must not be {@code null}.
1212 *
1213 * @return {@code true} if the entry was updated, or {@code false} because
1214 * the specified attribute already existed with all provided values.
1215 */
1216 public boolean addAttribute(final Attribute attribute)
1217 {
1218 ensureNotNull(attribute);
1219
1220 final String lowerName = toLowerCase(attribute.getName());
1221 final Attribute attr = attributes.get(lowerName);
1222 if (attr == null)
1223 {
1224 attributes.put(lowerName, attribute);
1225 return true;
1226 }
1227 else
1228 {
1229 final Attribute newAttr = Attribute.mergeAttributes(attr, attribute);
1230 attributes.put(lowerName, newAttr);
1231 return (attr.getRawValues().length != newAttr.getRawValues().length);
1232 }
1233 }
1234
1235
1236
1237 /**
1238 * Adds the specified attribute value to this entry, if it is not already
1239 * present.
1240 *
1241 * @param attributeName The name for the attribute to be added. It must
1242 * not be {@code null}.
1243 * @param attributeValue The value for the attribute to be added. It must
1244 * not be {@code null}.
1245 *
1246 * @return {@code true} if the entry was updated, or {@code false} because
1247 * the specified attribute already existed with the given value.
1248 */
1249 public boolean addAttribute(final String attributeName,
1250 final String attributeValue)
1251 {
1252 ensureNotNull(attributeName, attributeValue);
1253 return addAttribute(new Attribute(attributeName, schema, attributeValue));
1254 }
1255
1256
1257
1258 /**
1259 * Adds the specified attribute value to this entry, if it is not already
1260 * present.
1261 *
1262 * @param attributeName The name for the attribute to be added. It must
1263 * not be {@code null}.
1264 * @param attributeValue The value for the attribute to be added. It must
1265 * not be {@code null}.
1266 *
1267 * @return {@code true} if the entry was updated, or {@code false} because
1268 * the specified attribute already existed with the given value.
1269 */
1270 public boolean addAttribute(final String attributeName,
1271 final byte[] attributeValue)
1272 {
1273 ensureNotNull(attributeName, attributeValue);
1274 return addAttribute(new Attribute(attributeName, schema, attributeValue));
1275 }
1276
1277
1278
1279 /**
1280 * Adds the provided attribute to this entry. If this entry already contains
1281 * an attribute with the same name, then their values will be merged.
1282 *
1283 * @param attributeName The name for the attribute to be added. It must
1284 * not be {@code null}.
1285 * @param attributeValues The value for the attribute to be added. It must
1286 * not be {@code null}.
1287 *
1288 * @return {@code true} if the entry was updated, or {@code false} because
1289 * the specified attribute already existed with all provided values.
1290 */
1291 public boolean addAttribute(final String attributeName,
1292 final String... attributeValues)
1293 {
1294 ensureNotNull(attributeName, attributeValues);
1295 return addAttribute(new Attribute(attributeName, schema, attributeValues));
1296 }
1297
1298
1299
1300 /**
1301 * Adds the provided attribute to this entry. If this entry already contains
1302 * an attribute with the same name, then their values will be merged.
1303 *
1304 * @param attributeName The name for the attribute to be added. It must
1305 * not be {@code null}.
1306 * @param attributeValues The value for the attribute to be added. It must
1307 * not be {@code null}.
1308 *
1309 * @return {@code true} if the entry was updated, or {@code false} because
1310 * the specified attribute already existed with all provided values.
1311 */
1312 public boolean addAttribute(final String attributeName,
1313 final byte[]... attributeValues)
1314 {
1315 ensureNotNull(attributeName, attributeValues);
1316 return addAttribute(new Attribute(attributeName, schema, attributeValues));
1317 }
1318
1319
1320
1321 /**
1322 * Adds the provided attribute to this entry. If this entry already contains
1323 * an attribute with the same name, then their values will be merged.
1324 *
1325 * @param attributeName The name for the attribute to be added. It must
1326 * not be {@code null}.
1327 * @param attributeValues The value for the attribute to be added. It must
1328 * not be {@code null}.
1329 *
1330 * @return {@code true} if the entry was updated, or {@code false} because
1331 * the specified attribute already existed with all provided values.
1332 */
1333 public boolean addAttribute(final String attributeName,
1334 final Collection<String> attributeValues)
1335 {
1336 ensureNotNull(attributeName, attributeValues);
1337 return addAttribute(new Attribute(attributeName, schema, attributeValues));
1338 }
1339
1340
1341
1342 /**
1343 * Removes the specified attribute from this entry.
1344 *
1345 * @param attributeName The name of the attribute to remove. It must not be
1346 * {@code null}.
1347 *
1348 * @return {@code true} if the attribute was removed from the entry, or
1349 * {@code false} if it was not present.
1350 */
1351 public boolean removeAttribute(final String attributeName)
1352 {
1353 ensureNotNull(attributeName);
1354
1355 if (schema == null)
1356 {
1357 return (attributes.remove(toLowerCase(attributeName)) != null);
1358 }
1359 else
1360 {
1361 final Attribute a = getAttribute(attributeName, schema);
1362 if (a == null)
1363 {
1364 return false;
1365 }
1366 else
1367 {
1368 attributes.remove(toLowerCase(a.getName()));
1369 return true;
1370 }
1371 }
1372 }
1373
1374
1375
1376 /**
1377 * Removes the specified attribute value from this entry if it is present. If
1378 * it is the last value for the attribute, then the entire attribute will be
1379 * removed. If the specified value is not present, then no change will be
1380 * made.
1381 *
1382 * @param attributeName The name of the attribute from which to remove the
1383 * value. It must not be {@code null}.
1384 * @param attributeValue The value to remove from the attribute. It must
1385 * not be {@code null}.
1386 *
1387 * @return {@code true} if the attribute value was removed from the entry, or
1388 * {@code false} if it was not present.
1389 */
1390 public boolean removeAttributeValue(final String attributeName,
1391 final String attributeValue)
1392 {
1393 return removeAttributeValue(attributeName, attributeValue, null);
1394 }
1395
1396
1397
1398 /**
1399 * Removes the specified attribute value from this entry if it is present. If
1400 * it is the last value for the attribute, then the entire attribute will be
1401 * removed. If the specified value is not present, then no change will be
1402 * made.
1403 *
1404 * @param attributeName The name of the attribute from which to remove the
1405 * value. It must not be {@code null}.
1406 * @param attributeValue The value to remove from the attribute. It must
1407 * not be {@code null}.
1408 * @param matchingRule The matching rule to use for the attribute. It may
1409 * be {@code null} to use the matching rule associated
1410 * with the attribute.
1411 *
1412 * @return {@code true} if the attribute value was removed from the entry, or
1413 * {@code false} if it was not present.
1414 */
1415 public boolean removeAttributeValue(final String attributeName,
1416 final String attributeValue,
1417 final MatchingRule matchingRule)
1418 {
1419 ensureNotNull(attributeName, attributeValue);
1420
1421 final Attribute attr = getAttribute(attributeName, schema);
1422 if (attr == null)
1423 {
1424 return false;
1425 }
1426 else
1427 {
1428 final String lowerName = toLowerCase(attr.getName());
1429 final Attribute newAttr = Attribute.removeValues(attr,
1430 new Attribute(attributeName, attributeValue), matchingRule);
1431 if (newAttr.hasValue())
1432 {
1433 attributes.put(lowerName, newAttr);
1434 }
1435 else
1436 {
1437 attributes.remove(lowerName);
1438 }
1439
1440 return (attr.getRawValues().length != newAttr.getRawValues().length);
1441 }
1442 }
1443
1444
1445
1446 /**
1447 * Removes the specified attribute value from this entry if it is present. If
1448 * it is the last value for the attribute, then the entire attribute will be
1449 * removed. If the specified value is not present, then no change will be
1450 * made.
1451 *
1452 * @param attributeName The name of the attribute from which to remove the
1453 * value. It must not be {@code null}.
1454 * @param attributeValue The value to remove from the attribute. It must
1455 * not be {@code null}.
1456 *
1457 * @return {@code true} if the attribute value was removed from the entry, or
1458 * {@code false} if it was not present.
1459 */
1460 public boolean removeAttributeValue(final String attributeName,
1461 final byte[] attributeValue)
1462 {
1463 return removeAttributeValue(attributeName, attributeValue, null);
1464 }
1465
1466
1467
1468 /**
1469 * Removes the specified attribute value from this entry if it is present. If
1470 * it is the last value for the attribute, then the entire attribute will be
1471 * removed. If the specified value is not present, then no change will be
1472 * made.
1473 *
1474 * @param attributeName The name of the attribute from which to remove the
1475 * value. It must not be {@code null}.
1476 * @param attributeValue The value to remove from the attribute. It must
1477 * not be {@code null}.
1478 * @param matchingRule The matching rule to use for the attribute. It may
1479 * be {@code null} to use the matching rule associated
1480 * with the attribute.
1481 *
1482 * @return {@code true} if the attribute value was removed from the entry, or
1483 * {@code false} if it was not present.
1484 */
1485 public boolean removeAttributeValue(final String attributeName,
1486 final byte[] attributeValue,
1487 final MatchingRule matchingRule)
1488 {
1489 ensureNotNull(attributeName, attributeValue);
1490
1491 final Attribute attr = getAttribute(attributeName, schema);
1492 if (attr == null)
1493 {
1494 return false;
1495 }
1496 else
1497 {
1498 final String lowerName = toLowerCase(attr.getName());
1499 final Attribute newAttr = Attribute.removeValues(attr,
1500 new Attribute(attributeName, attributeValue), matchingRule);
1501 if (newAttr.hasValue())
1502 {
1503 attributes.put(lowerName, newAttr);
1504 }
1505 else
1506 {
1507 attributes.remove(lowerName);
1508 }
1509
1510 return (attr.getRawValues().length != newAttr.getRawValues().length);
1511 }
1512 }
1513
1514
1515
1516 /**
1517 * Removes the specified attribute values from this entry if they are present.
1518 * If the attribute does not have any remaining values, then the entire
1519 * attribute will be removed. If any of the provided values are not present,
1520 * then they will be ignored.
1521 *
1522 * @param attributeName The name of the attribute from which to remove the
1523 * values. It must not be {@code null}.
1524 * @param attributeValues The set of values to remove from the attribute.
1525 * It must not be {@code null}.
1526 *
1527 * @return {@code true} if any attribute values were removed from the entry,
1528 * or {@code false} none of them were present.
1529 */
1530 public boolean removeAttributeValues(final String attributeName,
1531 final String... attributeValues)
1532 {
1533 ensureNotNull(attributeName, attributeValues);
1534
1535 final Attribute attr = getAttribute(attributeName, schema);
1536 if (attr == null)
1537 {
1538 return false;
1539 }
1540 else
1541 {
1542 final String lowerName = toLowerCase(attr.getName());
1543 final Attribute newAttr = Attribute.removeValues(attr,
1544 new Attribute(attributeName, attributeValues));
1545 if (newAttr.hasValue())
1546 {
1547 attributes.put(lowerName, newAttr);
1548 }
1549 else
1550 {
1551 attributes.remove(lowerName);
1552 }
1553
1554 return (attr.getRawValues().length != newAttr.getRawValues().length);
1555 }
1556 }
1557
1558
1559
1560 /**
1561 * Removes the specified attribute values from this entry if they are present.
1562 * If the attribute does not have any remaining values, then the entire
1563 * attribute will be removed. If any of the provided values are not present,
1564 * then they will be ignored.
1565 *
1566 * @param attributeName The name of the attribute from which to remove the
1567 * values. It must not be {@code null}.
1568 * @param attributeValues The set of values to remove from the attribute.
1569 * It must not be {@code null}.
1570 *
1571 * @return {@code true} if any attribute values were removed from the entry,
1572 * or {@code false} none of them were present.
1573 */
1574 public boolean removeAttributeValues(final String attributeName,
1575 final byte[]... attributeValues)
1576 {
1577 ensureNotNull(attributeName, attributeValues);
1578
1579 final Attribute attr = getAttribute(attributeName, schema);
1580 if (attr == null)
1581 {
1582 return false;
1583 }
1584 else
1585 {
1586 final String lowerName = toLowerCase(attr.getName());
1587 final Attribute newAttr = Attribute.removeValues(attr,
1588 new Attribute(attributeName, attributeValues));
1589 if (newAttr.hasValue())
1590 {
1591 attributes.put(lowerName, newAttr);
1592 }
1593 else
1594 {
1595 attributes.remove(lowerName);
1596 }
1597
1598 return (attr.getRawValues().length != newAttr.getRawValues().length);
1599 }
1600 }
1601
1602
1603
1604 /**
1605 * Adds the provided attribute to this entry, replacing any existing set of
1606 * values for the associated attribute.
1607 *
1608 * @param attribute The attribute to be included in this entry. It must not
1609 * be {@code null}.
1610 */
1611 public void setAttribute(final Attribute attribute)
1612 {
1613 ensureNotNull(attribute);
1614
1615 final String lowerName;
1616 final Attribute a = getAttribute(attribute.getName(), schema);
1617 if (a == null)
1618 {
1619 lowerName = toLowerCase(attribute.getName());
1620 }
1621 else
1622 {
1623 lowerName = toLowerCase(a.getName());
1624 }
1625
1626 attributes.put(lowerName, attribute);
1627 }
1628
1629
1630
1631 /**
1632 * Adds the provided attribute to this entry, replacing any existing set of
1633 * values for the associated attribute.
1634 *
1635 * @param attributeName The name to use for the attribute. It must not be
1636 * {@code null}.
1637 * @param attributeValue The value to use for the attribute. It must not be
1638 * {@code null}.
1639 */
1640 public void setAttribute(final String attributeName,
1641 final String attributeValue)
1642 {
1643 ensureNotNull(attributeName, attributeValue);
1644 setAttribute(new Attribute(attributeName, schema, attributeValue));
1645 }
1646
1647
1648
1649 /**
1650 * Adds the provided attribute to this entry, replacing any existing set of
1651 * values for the associated attribute.
1652 *
1653 * @param attributeName The name to use for the attribute. It must not be
1654 * {@code null}.
1655 * @param attributeValue The value to use for the attribute. It must not be
1656 * {@code null}.
1657 */
1658 public void setAttribute(final String attributeName,
1659 final byte[] attributeValue)
1660 {
1661 ensureNotNull(attributeName, attributeValue);
1662 setAttribute(new Attribute(attributeName, schema, attributeValue));
1663 }
1664
1665
1666
1667 /**
1668 * Adds the provided attribute to this entry, replacing any existing set of
1669 * values for the associated attribute.
1670 *
1671 * @param attributeName The name to use for the attribute. It must not be
1672 * {@code null}.
1673 * @param attributeValues The set of values to use for the attribute. It
1674 * must not be {@code null}.
1675 */
1676 public void setAttribute(final String attributeName,
1677 final String... attributeValues)
1678 {
1679 ensureNotNull(attributeName, attributeValues);
1680 setAttribute(new Attribute(attributeName, schema, attributeValues));
1681 }
1682
1683
1684
1685 /**
1686 * Adds the provided attribute to this entry, replacing any existing set of
1687 * values for the associated attribute.
1688 *
1689 * @param attributeName The name to use for the attribute. It must not be
1690 * {@code null}.
1691 * @param attributeValues The set of values to use for the attribute. It
1692 * must not be {@code null}.
1693 */
1694 public void setAttribute(final String attributeName,
1695 final byte[]... attributeValues)
1696 {
1697 ensureNotNull(attributeName, attributeValues);
1698 setAttribute(new Attribute(attributeName, schema, attributeValues));
1699 }
1700
1701
1702
1703 /**
1704 * Adds the provided attribute to this entry, replacing any existing set of
1705 * values for the associated attribute.
1706 *
1707 * @param attributeName The name to use for the attribute. It must not be
1708 * {@code null}.
1709 * @param attributeValues The set of values to use for the attribute. It
1710 * must not be {@code null}.
1711 */
1712 public void setAttribute(final String attributeName,
1713 final Collection<String> attributeValues)
1714 {
1715 ensureNotNull(attributeName, attributeValues);
1716 setAttribute(new Attribute(attributeName, schema, attributeValues));
1717 }
1718
1719
1720
1721 /**
1722 * Indicates whether this entry falls within the range of the provided search
1723 * base DN and scope.
1724 *
1725 * @param baseDN The base DN for which to make the determination. It must
1726 * not be {@code null}.
1727 * @param scope The scope for which to make the determination. It must not
1728 * be {@code null}.
1729 *
1730 * @return {@code true} if this entry is within the range of the provided
1731 * base and scope, or {@code false} if not.
1732 *
1733 * @throws LDAPException If a problem occurs while making the determination.
1734 */
1735 public boolean matchesBaseAndScope(final String baseDN,
1736 final SearchScope scope)
1737 throws LDAPException
1738 {
1739 return getParsedDN().matchesBaseAndScope(new DN(baseDN), scope);
1740 }
1741
1742
1743
1744 /**
1745 * Indicates whether this entry falls within the range of the provided search
1746 * base DN and scope.
1747 *
1748 * @param baseDN The base DN for which to make the determination. It must
1749 * not be {@code null}.
1750 * @param scope The scope for which to make the determination. It must not
1751 * be {@code null}.
1752 *
1753 * @return {@code true} if this entry is within the range of the provided
1754 * base and scope, or {@code false} if not.
1755 *
1756 * @throws LDAPException If a problem occurs while making the determination.
1757 */
1758 public boolean matchesBaseAndScope(final DN baseDN, final SearchScope scope)
1759 throws LDAPException
1760 {
1761 return getParsedDN().matchesBaseAndScope(baseDN, scope);
1762 }
1763
1764
1765
1766 /**
1767 * Retrieves a set of modifications that can be applied to the source entry in
1768 * order to make it match the target entry. The diff will be generated in
1769 * reversible form (i.e., the same as calling
1770 * {@code diff(sourceEntry, targetEntry, ignoreRDN, true, attributes)}.
1771 *
1772 * @param sourceEntry The source entry for which the set of modifications
1773 * should be generated.
1774 * @param targetEntry The target entry, which is what the source entry
1775 * should look like if the returned modifications are
1776 * applied.
1777 * @param ignoreRDN Indicates whether to ignore differences in the RDNs
1778 * of the provided entries. If this is {@code false},
1779 * then the resulting set of modifications may include
1780 * changes to the RDN attribute. If it is {@code true},
1781 * then differences in the entry DNs will be ignored.
1782 * @param attributes The set of attributes to be compared. If this is
1783 * {@code null} or empty, then all attributes will be
1784 * compared. Note that if a list of attributes is
1785 * specified, then matching will be performed only
1786 * against the attribute base name and any differences in
1787 * attribute options will be ignored.
1788 *
1789 * @return A set of modifications that can be applied to the source entry in
1790 * order to make it match the target entry.
1791 */
1792 public static List<Modification> diff(final Entry sourceEntry,
1793 final Entry targetEntry,
1794 final boolean ignoreRDN,
1795 final String... attributes)
1796 {
1797 return diff(sourceEntry, targetEntry, ignoreRDN, true, attributes);
1798 }
1799
1800
1801
1802 /**
1803 * Retrieves a set of modifications that can be applied to the source entry in
1804 * order to make it match the target entry.
1805 *
1806 * @param sourceEntry The source entry for which the set of modifications
1807 * should be generated.
1808 * @param targetEntry The target entry, which is what the source entry
1809 * should look like if the returned modifications are
1810 * applied.
1811 * @param ignoreRDN Indicates whether to ignore differences in the RDNs
1812 * of the provided entries. If this is {@code false},
1813 * then the resulting set of modifications may include
1814 * changes to the RDN attribute. If it is {@code true},
1815 * then differences in the entry DNs will be ignored.
1816 * @param reversible Indicates whether to generate the diff in reversible
1817 * form. In reversible form, only the ADD or DELETE
1818 * modification types will be used so that source entry
1819 * could be reconstructed from the target and the
1820 * resulting modifications. In non-reversible form, only
1821 * the REPLACE modification type will be used. Attempts
1822 * to apply the modifications obtained when using
1823 * reversible form are more likely to fail if the entry
1824 * has been modified since the source and target forms
1825 * were obtained.
1826 * @param attributes The set of attributes to be compared. If this is
1827 * {@code null} or empty, then all attributes will be
1828 * compared. Note that if a list of attributes is
1829 * specified, then matching will be performed only
1830 * against the attribute base name and any differences in
1831 * attribute options will be ignored.
1832 *
1833 * @return A set of modifications that can be applied to the source entry in
1834 * order to make it match the target entry.
1835 */
1836 public static List<Modification> diff(final Entry sourceEntry,
1837 final Entry targetEntry,
1838 final boolean ignoreRDN,
1839 final boolean reversible,
1840 final String... attributes)
1841 {
1842 HashSet<String> compareAttrs = null;
1843 if ((attributes != null) && (attributes.length > 0))
1844 {
1845 compareAttrs = new HashSet<String>(attributes.length);
1846 for (final String s : attributes)
1847 {
1848 compareAttrs.add(toLowerCase(Attribute.getBaseName(s)));
1849 }
1850 }
1851
1852 final LinkedHashMap<String,Attribute> sourceOnlyAttrs =
1853 new LinkedHashMap<String,Attribute>();
1854 final LinkedHashMap<String,Attribute> targetOnlyAttrs =
1855 new LinkedHashMap<String,Attribute>();
1856 final LinkedHashMap<String,Attribute> commonAttrs =
1857 new LinkedHashMap<String,Attribute>();
1858
1859 for (final Map.Entry<String,Attribute> e :
1860 sourceEntry.attributes.entrySet())
1861 {
1862 final String lowerName = toLowerCase(e.getKey());
1863 if ((compareAttrs != null) &&
1864 (! compareAttrs.contains(Attribute.getBaseName(lowerName))))
1865 {
1866 continue;
1867 }
1868
1869 sourceOnlyAttrs.put(lowerName, e.getValue());
1870 commonAttrs.put(lowerName, e.getValue());
1871 }
1872
1873 for (final Map.Entry<String,Attribute> e :
1874 targetEntry.attributes.entrySet())
1875 {
1876 final String lowerName = toLowerCase(e.getKey());
1877 if ((compareAttrs != null) &&
1878 (! compareAttrs.contains(Attribute.getBaseName(lowerName))))
1879 {
1880 continue;
1881 }
1882
1883
1884 if (sourceOnlyAttrs.remove(lowerName) == null)
1885 {
1886 // It wasn't in the set of source attributes, so it must be a
1887 // target-only attribute.
1888 targetOnlyAttrs.put(lowerName,e.getValue());
1889 }
1890 }
1891
1892 for (final String lowerName : sourceOnlyAttrs.keySet())
1893 {
1894 commonAttrs.remove(lowerName);
1895 }
1896
1897 RDN sourceRDN = null;
1898 RDN targetRDN = null;
1899 if (ignoreRDN)
1900 {
1901 try
1902 {
1903 sourceRDN = sourceEntry.getRDN();
1904 }
1905 catch (Exception e)
1906 {
1907 debugException(e);
1908 }
1909
1910 try
1911 {
1912 targetRDN = targetEntry.getRDN();
1913 }
1914 catch (Exception e)
1915 {
1916 debugException(e);
1917 }
1918 }
1919
1920 final ArrayList<Modification> mods = new ArrayList<Modification>(10);
1921
1922 for (final Attribute a : sourceOnlyAttrs.values())
1923 {
1924 if (reversible)
1925 {
1926 ASN1OctetString[] values = a.getRawValues();
1927 if ((sourceRDN != null) && (sourceRDN.hasAttribute(a.getName())))
1928 {
1929 final ArrayList<ASN1OctetString> newValues =
1930 new ArrayList<ASN1OctetString>(values.length);
1931 for (final ASN1OctetString value : values)
1932 {
1933 if (! sourceRDN.hasAttributeValue(a.getName(), value.getValue()))
1934 {
1935 newValues.add(value);
1936 }
1937 }
1938
1939 if (newValues.isEmpty())
1940 {
1941 continue;
1942 }
1943 else
1944 {
1945 values = new ASN1OctetString[newValues.size()];
1946 newValues.toArray(values);
1947 }
1948 }
1949
1950 mods.add(new Modification(ModificationType.DELETE, a.getName(),
1951 values));
1952 }
1953 else
1954 {
1955 mods.add(new Modification(ModificationType.REPLACE, a.getName()));
1956 }
1957 }
1958
1959 for (final Attribute a : targetOnlyAttrs.values())
1960 {
1961 ASN1OctetString[] values = a.getRawValues();
1962 if ((targetRDN != null) && (targetRDN.hasAttribute(a.getName())))
1963 {
1964 final ArrayList<ASN1OctetString> newValues =
1965 new ArrayList<ASN1OctetString>(values.length);
1966 for (final ASN1OctetString value : values)
1967 {
1968 if (! targetRDN.hasAttributeValue(a.getName(), value.getValue()))
1969 {
1970 newValues.add(value);
1971 }
1972 }
1973
1974 if (newValues.isEmpty())
1975 {
1976 continue;
1977 }
1978 else
1979 {
1980 values = new ASN1OctetString[newValues.size()];
1981 newValues.toArray(values);
1982 }
1983 }
1984
1985 if (reversible)
1986 {
1987 mods.add(new Modification(ModificationType.ADD, a.getName(), values));
1988 }
1989 else
1990 {
1991 mods.add(new Modification(ModificationType.REPLACE, a.getName(),
1992 values));
1993 }
1994 }
1995
1996 for (final Attribute sourceAttr : commonAttrs.values())
1997 {
1998 final Attribute targetAttr =
1999 targetEntry.getAttribute(sourceAttr.getName());
2000 if (sourceAttr.equals(targetAttr))
2001 {
2002 continue;
2003 }
2004
2005 if (reversible ||
2006 ((targetRDN != null) && targetRDN.hasAttribute(targetAttr.getName())))
2007 {
2008 final ASN1OctetString[] sourceValueArray = sourceAttr.getRawValues();
2009 final LinkedHashMap<ASN1OctetString,ASN1OctetString> sourceValues =
2010 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
2011 sourceValueArray.length);
2012 for (final ASN1OctetString s : sourceValueArray)
2013 {
2014 try
2015 {
2016 sourceValues.put(sourceAttr.getMatchingRule().normalize(s), s);
2017 }
2018 catch (final Exception e)
2019 {
2020 debugException(e);
2021 sourceValues.put(s, s);
2022 }
2023 }
2024
2025 final ASN1OctetString[] targetValueArray = targetAttr.getRawValues();
2026 final LinkedHashMap<ASN1OctetString,ASN1OctetString> targetValues =
2027 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
2028 targetValueArray.length);
2029 for (final ASN1OctetString s : targetValueArray)
2030 {
2031 try
2032 {
2033 targetValues.put(sourceAttr.getMatchingRule().normalize(s), s);
2034 }
2035 catch (final Exception e)
2036 {
2037 debugException(e);
2038 targetValues.put(s, s);
2039 }
2040 }
2041
2042 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
2043 sourceIterator = sourceValues.entrySet().iterator();
2044 while (sourceIterator.hasNext())
2045 {
2046 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
2047 sourceIterator.next();
2048 if (targetValues.remove(e.getKey()) != null)
2049 {
2050 sourceIterator.remove();
2051 }
2052 else if ((sourceRDN != null) &&
2053 sourceRDN.hasAttributeValue(sourceAttr.getName(),
2054 e.getValue().getValue()))
2055 {
2056 sourceIterator.remove();
2057 }
2058 }
2059
2060 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
2061 targetIterator = targetValues.entrySet().iterator();
2062 while (targetIterator.hasNext())
2063 {
2064 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
2065 targetIterator.next();
2066 if ((targetRDN != null) &&
2067 targetRDN.hasAttributeValue(targetAttr.getName(),
2068 e.getValue().getValue()))
2069 {
2070 targetIterator.remove();
2071 }
2072 }
2073
2074 final ArrayList<ASN1OctetString> addValues =
2075 new ArrayList<ASN1OctetString>(targetValues.values());
2076 final ArrayList<ASN1OctetString> delValues =
2077 new ArrayList<ASN1OctetString>(sourceValues.values());
2078
2079 if (! addValues.isEmpty())
2080 {
2081 final ASN1OctetString[] addArray =
2082 new ASN1OctetString[addValues.size()];
2083 mods.add(new Modification(ModificationType.ADD, targetAttr.getName(),
2084 addValues.toArray(addArray)));
2085 }
2086
2087 if (! delValues.isEmpty())
2088 {
2089 final ASN1OctetString[] delArray =
2090 new ASN1OctetString[delValues.size()];
2091 mods.add(new Modification(ModificationType.DELETE,
2092 sourceAttr.getName(), delValues.toArray(delArray)));
2093 }
2094 }
2095 else
2096 {
2097 mods.add(new Modification(ModificationType.REPLACE,
2098 targetAttr.getName(), targetAttr.getRawValues()));
2099 }
2100 }
2101
2102 return mods;
2103 }
2104
2105
2106
2107 /**
2108 * Merges the contents of all provided entries so that the resulting entry
2109 * will contain all attribute values present in at least one of the entries.
2110 *
2111 * @param entries The set of entries to be merged. At least one entry must
2112 * be provided.
2113 *
2114 * @return An entry containing all attribute values present in at least one
2115 * of the entries.
2116 */
2117 public static Entry mergeEntries(final Entry... entries)
2118 {
2119 ensureNotNull(entries);
2120 ensureTrue(entries.length > 0);
2121
2122 final Entry newEntry = entries[0].duplicate();
2123
2124 for (int i=1; i < entries.length; i++)
2125 {
2126 for (final Attribute a : entries[i].attributes.values())
2127 {
2128 newEntry.addAttribute(a);
2129 }
2130 }
2131
2132 return newEntry;
2133 }
2134
2135
2136
2137 /**
2138 * Intersects the contents of all provided entries so that the resulting
2139 * entry will contain only attribute values present in all of the provided
2140 * entries.
2141 *
2142 * @param entries The set of entries to be intersected. At least one entry
2143 * must be provided.
2144 *
2145 * @return An entry containing only attribute values contained in all of the
2146 * provided entries.
2147 */
2148 public static Entry intersectEntries(final Entry... entries)
2149 {
2150 ensureNotNull(entries);
2151 ensureTrue(entries.length > 0);
2152
2153 final Entry newEntry = entries[0].duplicate();
2154
2155 for (final Attribute a : entries[0].attributes.values())
2156 {
2157 final String name = a.getName();
2158 for (final byte[] v : a.getValueByteArrays())
2159 {
2160 for (int i=1; i < entries.length; i++)
2161 {
2162 if (! entries[i].hasAttributeValue(name, v))
2163 {
2164 newEntry.removeAttributeValue(name, v);
2165 break;
2166 }
2167 }
2168 }
2169 }
2170
2171 return newEntry;
2172 }
2173
2174
2175
2176 /**
2177 * Creates a duplicate of the provided entry with the given set of
2178 * modifications applied to it.
2179 *
2180 * @param entry The entry to be modified. It must not be
2181 * {@code null}.
2182 * @param lenient Indicates whether to exhibit a lenient behavior for
2183 * the modifications, which will cause it to ignore
2184 * problems like trying to add values that already
2185 * exist or to remove nonexistent attributes or values.
2186 * @param modifications The set of modifications to apply to the entry. It
2187 * must not be {@code null} or empty.
2188 *
2189 * @return An updated version of the entry with the requested modifications
2190 * applied.
2191 *
2192 * @throws LDAPException If a problem occurs while attempting to apply the
2193 * modifications.
2194 */
2195 public static Entry applyModifications(final Entry entry,
2196 final boolean lenient,
2197 final Modification... modifications)
2198 throws LDAPException
2199 {
2200 ensureNotNull(entry, modifications);
2201 ensureFalse(modifications.length == 0);
2202
2203 return applyModifications(entry, lenient, Arrays.asList(modifications));
2204 }
2205
2206
2207
2208 /**
2209 * Creates a duplicate of the provided entry with the given set of
2210 * modifications applied to it.
2211 *
2212 * @param entry The entry to be modified. It must not be
2213 * {@code null}.
2214 * @param lenient Indicates whether to exhibit a lenient behavior for
2215 * the modifications, which will cause it to ignore
2216 * problems like trying to add values that already
2217 * exist or to remove nonexistent attributes or values.
2218 * @param modifications The set of modifications to apply to the entry. It
2219 * must not be {@code null} or empty.
2220 *
2221 * @return An updated version of the entry with the requested modifications
2222 * applied.
2223 *
2224 * @throws LDAPException If a problem occurs while attempting to apply the
2225 * modifications.
2226 */
2227 public static Entry applyModifications(final Entry entry,
2228 final boolean lenient,
2229 final List<Modification> modifications)
2230 throws LDAPException
2231 {
2232 ensureNotNull(entry, modifications);
2233 ensureFalse(modifications.isEmpty());
2234
2235 final Entry e = entry.duplicate();
2236 final ArrayList<String> errors =
2237 new ArrayList<String>(modifications.size());
2238 ResultCode resultCode = null;
2239
2240 // Get the RDN for the entry to ensure that RDN modifications are not
2241 // allowed.
2242 RDN rdn = null;
2243 try
2244 {
2245 rdn = entry.getRDN();
2246 }
2247 catch (final LDAPException le)
2248 {
2249 debugException(le);
2250 }
2251
2252 for (final Modification m : modifications)
2253 {
2254 final String name = m.getAttributeName();
2255 final byte[][] values = m.getValueByteArrays();
2256 switch (m.getModificationType().intValue())
2257 {
2258 case ModificationType.ADD_INT_VALUE:
2259 if (lenient)
2260 {
2261 e.addAttribute(m.getAttribute());
2262 }
2263 else
2264 {
2265 if (values.length == 0)
2266 {
2267 errors.add(ERR_ENTRY_APPLY_MODS_ADD_NO_VALUES.get(name));
2268 }
2269
2270 for (int i=0; i < values.length; i++)
2271 {
2272 if (! e.addAttribute(name, values[i]))
2273 {
2274 if (resultCode == null)
2275 {
2276 resultCode = ResultCode.ATTRIBUTE_OR_VALUE_EXISTS;
2277 }
2278 errors.add(ERR_ENTRY_APPLY_MODS_ADD_EXISTING.get(
2279 m.getValues()[i], name));
2280 }
2281 }
2282 }
2283 break;
2284
2285 case ModificationType.DELETE_INT_VALUE:
2286 if (values.length == 0)
2287 {
2288 final boolean removed = e.removeAttribute(name);
2289 if (! (lenient || removed))
2290 {
2291 if (resultCode == null)
2292 {
2293 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2294 }
2295 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_ATTR.get(
2296 name));
2297 }
2298 }
2299 else
2300 {
2301 for (int i=0; i < values.length; i++)
2302 {
2303 final boolean removed = e.removeAttributeValue(name, values[i]);
2304 if (! (lenient || removed))
2305 {
2306 if (resultCode == null)
2307 {
2308 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2309 }
2310 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_VALUE.get(
2311 m.getValues()[i], name));
2312 }
2313 }
2314 }
2315 break;
2316
2317 case ModificationType.REPLACE_INT_VALUE:
2318 if (values.length == 0)
2319 {
2320 e.removeAttribute(name);
2321 }
2322 else
2323 {
2324 e.setAttribute(m.getAttribute());
2325 }
2326 break;
2327
2328 case ModificationType.INCREMENT_INT_VALUE:
2329 final Attribute a = e.getAttribute(name);
2330 if ((a == null) || (! a.hasValue()))
2331 {
2332 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_SUCH_ATTR.get(name));
2333 continue;
2334 }
2335
2336 if (a.size() > 1)
2337 {
2338 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NOT_SINGLE_VALUED.get(
2339 name));
2340 continue;
2341 }
2342
2343 if ((rdn != null) && rdn.hasAttribute(name))
2344 {
2345 final String msg =
2346 ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN());
2347 if (! errors.contains(msg))
2348 {
2349 errors.add(msg);
2350 }
2351
2352 if (resultCode == null)
2353 {
2354 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2355 }
2356 continue;
2357 }
2358
2359 final BigInteger currentValue;
2360 try
2361 {
2362 currentValue = new BigInteger(a.getValue());
2363 }
2364 catch (NumberFormatException nfe)
2365 {
2366 debugException(nfe);
2367 errors.add(
2368 ERR_ENTRY_APPLY_MODS_INCREMENT_ENTRY_VALUE_NOT_INTEGER.get(
2369 name, a.getValue()));
2370 continue;
2371 }
2372
2373 if (values.length == 0)
2374 {
2375 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_MOD_VALUES.get(name));
2376 continue;
2377 }
2378 else if (values.length > 1)
2379 {
2380 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MULTIPLE_MOD_VALUES.get(
2381 name));
2382 continue;
2383 }
2384
2385 final BigInteger incrementValue;
2386 final String incrementValueStr = m.getValues()[0];
2387 try
2388 {
2389 incrementValue = new BigInteger(incrementValueStr);
2390 }
2391 catch (NumberFormatException nfe)
2392 {
2393 debugException(nfe);
2394 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MOD_VALUE_NOT_INTEGER.get(
2395 name, incrementValueStr));
2396 continue;
2397 }
2398
2399 final BigInteger newValue = currentValue.add(incrementValue);
2400 e.setAttribute(name, newValue.toString());
2401 break;
2402
2403 default:
2404 errors.add(ERR_ENTRY_APPLY_MODS_UNKNOWN_TYPE.get(
2405 String.valueOf(m.getModificationType())));
2406 break;
2407 }
2408 }
2409
2410
2411 // Make sure that the entry still has all of the RDN attribute values.
2412 if (rdn != null)
2413 {
2414 final String[] rdnAttrs = rdn.getAttributeNames();
2415 final byte[][] rdnValues = rdn.getByteArrayAttributeValues();
2416 for (int i=0; i < rdnAttrs.length; i++)
2417 {
2418 if (! e.hasAttributeValue(rdnAttrs[i], rdnValues[i]))
2419 {
2420 errors.add(ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()));
2421 if (resultCode == null)
2422 {
2423 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2424 }
2425 break;
2426 }
2427 }
2428 }
2429
2430
2431 if (errors.isEmpty())
2432 {
2433 return e;
2434 }
2435
2436 if (resultCode == null)
2437 {
2438 resultCode = ResultCode.CONSTRAINT_VIOLATION;
2439 }
2440
2441 throw new LDAPException(resultCode,
2442 ERR_ENTRY_APPLY_MODS_FAILURE.get(e.getDN(),
2443 concatenateStrings(errors)));
2444 }
2445
2446
2447
2448 /**
2449 * Creates a duplicate of the provided entry with the appropriate changes for
2450 * a modify DN operation. Any corresponding changes to the set of attribute
2451 * values (to ensure that the new RDN values are present in the entry, and
2452 * optionally to remove the old RDN values from the entry) will also be
2453 * applied.
2454 *
2455 * @param entry The entry to be renamed. It must not be
2456 * {@code null}.
2457 * @param newRDN The new RDN to use for the entry. It must not be
2458 * {@code null}.
2459 * @param deleteOldRDN Indicates whether attribute values that were present
2460 * in the old RDN but are no longer present in the new
2461 * DN should be removed from the entry.
2462 *
2463 * @return A new entry that is a duplicate of the provided entry, except with
2464 * any necessary changes for the modify DN.
2465 *
2466 * @throws LDAPException If a problem is encountered during modify DN
2467 * processing.
2468 */
2469 public static Entry applyModifyDN(final Entry entry, final String newRDN,
2470 final boolean deleteOldRDN)
2471 throws LDAPException
2472 {
2473 return applyModifyDN(entry, newRDN, deleteOldRDN, null);
2474 }
2475
2476
2477
2478 /**
2479 * Creates a duplicate of the provided entry with the appropriate changes for
2480 * a modify DN operation. Any corresponding changes to the set of attribute
2481 * values (to ensure that the new RDN values are present in the entry, and
2482 * optionally to remove the old RDN values from the entry) will also be
2483 * applied.
2484 *
2485 * @param entry The entry to be renamed. It must not be
2486 * {@code null}.
2487 * @param newRDN The new RDN to use for the entry. It must not be
2488 * {@code null}.
2489 * @param deleteOldRDN Indicates whether attribute values that were present
2490 * in the old RDN but are no longer present in the new
2491 * DN should be removed from the entry.
2492 * @param newSuperiorDN The new superior DN for the entry. If this is
2493 * {@code null}, then the entry will remain below its
2494 * existing parent. If it is non-{@code null}, then
2495 * the resulting DN will be a concatenation of the new
2496 * RDN and the new superior DN.
2497 *
2498 * @return A new entry that is a duplicate of the provided entry, except with
2499 * any necessary changes for the modify DN.
2500 *
2501 * @throws LDAPException If a problem is encountered during modify DN
2502 * processing.
2503 */
2504 public static Entry applyModifyDN(final Entry entry, final String newRDN,
2505 final boolean deleteOldRDN,
2506 final String newSuperiorDN)
2507 throws LDAPException
2508 {
2509 ensureNotNull(entry);
2510 ensureNotNull(newRDN);
2511
2512 // Parse all of the necessary elements from the request.
2513 final DN parsedOldDN = entry.getParsedDN();
2514 final RDN parsedOldRDN = parsedOldDN.getRDN();
2515 final DN parsedOldSuperiorDN = parsedOldDN.getParent();
2516
2517 final RDN parsedNewRDN = new RDN(newRDN);
2518
2519 final DN parsedNewSuperiorDN;
2520 if (newSuperiorDN == null)
2521 {
2522 parsedNewSuperiorDN = parsedOldSuperiorDN;
2523 }
2524 else
2525 {
2526 parsedNewSuperiorDN = new DN(newSuperiorDN);
2527 }
2528
2529 // Duplicate the provided entry and update it with the new DN.
2530 final Entry newEntry = entry.duplicate();
2531 if (parsedNewSuperiorDN == null)
2532 {
2533 // This should only happen if the provided entry has a zero-length DN.
2534 // It's extremely unlikely that a directory server would permit this
2535 // change, but we'll go ahead and process it.
2536 newEntry.setDN(new DN(parsedNewRDN));
2537 }
2538 else
2539 {
2540 newEntry.setDN(new DN(parsedNewRDN, parsedNewSuperiorDN));
2541 }
2542
2543 // If deleteOldRDN is true, then remove any values present in the old RDN
2544 // that are not present in the new RDN.
2545 if (deleteOldRDN && (parsedOldRDN != null))
2546 {
2547 final String[] oldNames = parsedOldRDN.getAttributeNames();
2548 final byte[][] oldValues = parsedOldRDN.getByteArrayAttributeValues();
2549 for (int i=0; i < oldNames.length; i++)
2550 {
2551 if (! parsedNewRDN.hasAttributeValue(oldNames[i], oldValues[i]))
2552 {
2553 newEntry.removeAttributeValue(oldNames[i], oldValues[i]);
2554 }
2555 }
2556 }
2557
2558 // Add any values present in the new RDN that were not present in the old
2559 // RDN.
2560 final String[] newNames = parsedNewRDN.getAttributeNames();
2561 final byte[][] newValues = parsedNewRDN.getByteArrayAttributeValues();
2562 for (int i=0; i < newNames.length; i++)
2563 {
2564 if ((parsedOldRDN == null) ||
2565 (! parsedOldRDN.hasAttributeValue(newNames[i], newValues[i])))
2566 {
2567 newEntry.addAttribute(newNames[i], newValues[i]);
2568 }
2569 }
2570
2571 return newEntry;
2572 }
2573
2574
2575
2576 /**
2577 * Generates a hash code for this entry.
2578 *
2579 * @return The generated hash code for this entry.
2580 */
2581 @Override()
2582 public int hashCode()
2583 {
2584 int hashCode = 0;
2585 try
2586 {
2587 hashCode += getParsedDN().hashCode();
2588 }
2589 catch (LDAPException le)
2590 {
2591 debugException(le);
2592 hashCode += dn.hashCode();
2593 }
2594
2595 for (final Attribute a : attributes.values())
2596 {
2597 hashCode += a.hashCode();
2598 }
2599
2600 return hashCode;
2601 }
2602
2603
2604
2605 /**
2606 * Indicates whether the provided object is equal to this entry. The provided
2607 * object will only be considered equal to this entry if it is an entry with
2608 * the same DN and set of attributes.
2609 *
2610 * @param o The object for which to make the determination.
2611 *
2612 * @return {@code true} if the provided object is considered equal to this
2613 * entry, or {@code false} if not.
2614 */
2615 @Override()
2616 public boolean equals(final Object o)
2617 {
2618 if (o == null)
2619 {
2620 return false;
2621 }
2622
2623 if (o == this)
2624 {
2625 return true;
2626 }
2627
2628 if (! (o instanceof Entry))
2629 {
2630 return false;
2631 }
2632
2633 final Entry e = (Entry) o;
2634
2635 try
2636 {
2637 final DN thisDN = getParsedDN();
2638 final DN thatDN = e.getParsedDN();
2639 if (! thisDN.equals(thatDN))
2640 {
2641 return false;
2642 }
2643 }
2644 catch (LDAPException le)
2645 {
2646 debugException(le);
2647 if (! dn.equals(e.dn))
2648 {
2649 return false;
2650 }
2651 }
2652
2653 if (attributes.size() != e.attributes.size())
2654 {
2655 return false;
2656 }
2657
2658 for (final Attribute a : attributes.values())
2659 {
2660 if (! e.hasAttribute(a))
2661 {
2662 return false;
2663 }
2664 }
2665
2666 return true;
2667 }
2668
2669
2670
2671 /**
2672 * Creates a new entry that is a duplicate of this entry.
2673 *
2674 * @return A new entry that is a duplicate of this entry.
2675 */
2676 public Entry duplicate()
2677 {
2678 return new Entry(dn, schema, attributes.values());
2679 }
2680
2681
2682
2683 /**
2684 * Retrieves an LDIF representation of this entry, with each attribute value
2685 * on a separate line. Long lines will not be wrapped.
2686 *
2687 * @return An LDIF representation of this entry.
2688 */
2689 public final String[] toLDIF()
2690 {
2691 return toLDIF(0);
2692 }
2693
2694
2695
2696 /**
2697 * Retrieves an LDIF representation of this entry, with each attribute value
2698 * on a separate line. Long lines will be wrapped at the specified column.
2699 *
2700 * @param wrapColumn The column at which long lines should be wrapped. A
2701 * value less than or equal to two indicates that no
2702 * wrapping should be performed.
2703 *
2704 * @return An LDIF representation of this entry.
2705 */
2706 public final String[] toLDIF(final int wrapColumn)
2707 {
2708 List<String> ldifLines = new ArrayList<String>(2*attributes.size());
2709 encodeNameAndValue("dn", new ASN1OctetString(dn), ldifLines);
2710
2711 for (final Attribute a : attributes.values())
2712 {
2713 final String name = a.getName();
2714 for (final ASN1OctetString value : a.getRawValues())
2715 {
2716 encodeNameAndValue(name, value, ldifLines);
2717 }
2718 }
2719
2720 if (wrapColumn > 2)
2721 {
2722 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines);
2723 }
2724
2725 final String[] lineArray = new String[ldifLines.size()];
2726 ldifLines.toArray(lineArray);
2727 return lineArray;
2728 }
2729
2730
2731
2732 /**
2733 * Encodes the provided name and value and adds the result to the provided
2734 * list of lines. This will handle the case in which the encoded name and
2735 * value includes comments about the base64-decoded representation of the
2736 * provided value.
2737 *
2738 * @param name The attribute name to be encoded.
2739 * @param value The attribute value to be encoded.
2740 * @param lines The list of lines to be updated.
2741 */
2742 private static void encodeNameAndValue(final String name,
2743 final ASN1OctetString value,
2744 final List<String> lines)
2745 {
2746 final String line = LDIFWriter.encodeNameAndValue(name, value);
2747 if (LDIFWriter.commentAboutBase64EncodedValues() &&
2748 line.startsWith(name + "::"))
2749 {
2750 final StringTokenizer tokenizer = new StringTokenizer(line, "\r\n");
2751 while (tokenizer.hasMoreTokens())
2752 {
2753 lines.add(tokenizer.nextToken());
2754 }
2755 }
2756 else
2757 {
2758 lines.add(line);
2759 }
2760 }
2761
2762
2763
2764 /**
2765 * Appends an LDIF representation of this entry to the provided buffer. Long
2766 * lines will not be wrapped.
2767 *
2768 * @param buffer The buffer to which the LDIF representation of this entry
2769 * should be written.
2770 */
2771 public final void toLDIF(final ByteStringBuffer buffer)
2772 {
2773 toLDIF(buffer, 0);
2774 }
2775
2776
2777
2778 /**
2779 * Appends an LDIF representation of this entry to the provided buffer.
2780 *
2781 * @param buffer The buffer to which the LDIF representation of this
2782 * entry should be written.
2783 * @param wrapColumn The column at which long lines should be wrapped. A
2784 * value less than or equal to two indicates that no
2785 * wrapping should be performed.
2786 */
2787 public final void toLDIF(final ByteStringBuffer buffer, final int wrapColumn)
2788 {
2789 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2790 wrapColumn);
2791 buffer.append(EOL_BYTES);
2792
2793 for (final Attribute a : attributes.values())
2794 {
2795 final String name = a.getName();
2796 for (final ASN1OctetString value : a.getRawValues())
2797 {
2798 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2799 buffer.append(EOL_BYTES);
2800 }
2801 }
2802 }
2803
2804
2805
2806 /**
2807 * Retrieves an LDIF-formatted string representation of this entry. No
2808 * wrapping will be performed, and no extra blank lines will be added.
2809 *
2810 * @return An LDIF-formatted string representation of this entry.
2811 */
2812 public final String toLDIFString()
2813 {
2814 final StringBuilder buffer = new StringBuilder();
2815 toLDIFString(buffer, 0);
2816 return buffer.toString();
2817 }
2818
2819
2820
2821 /**
2822 * Retrieves an LDIF-formatted string representation of this entry. No
2823 * extra blank lines will be added.
2824 *
2825 * @param wrapColumn The column at which long lines should be wrapped. A
2826 * value less than or equal to two indicates that no
2827 * wrapping should be performed.
2828 *
2829 * @return An LDIF-formatted string representation of this entry.
2830 */
2831 public final String toLDIFString(final int wrapColumn)
2832 {
2833 final StringBuilder buffer = new StringBuilder();
2834 toLDIFString(buffer, wrapColumn);
2835 return buffer.toString();
2836 }
2837
2838
2839
2840 /**
2841 * Appends an LDIF-formatted string representation of this entry to the
2842 * provided buffer. No wrapping will be performed, and no extra blank lines
2843 * will be added.
2844 *
2845 * @param buffer The buffer to which to append the LDIF representation of
2846 * this entry.
2847 */
2848 public final void toLDIFString(final StringBuilder buffer)
2849 {
2850 toLDIFString(buffer, 0);
2851 }
2852
2853
2854
2855 /**
2856 * Appends an LDIF-formatted string representation of this entry to the
2857 * provided buffer. No extra blank lines will be added.
2858 *
2859 * @param buffer The buffer to which to append the LDIF representation
2860 * of this entry.
2861 * @param wrapColumn The column at which long lines should be wrapped. A
2862 * value less than or equal to two indicates that no
2863 * wrapping should be performed.
2864 */
2865 public final void toLDIFString(final StringBuilder buffer,
2866 final int wrapColumn)
2867 {
2868 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2869 wrapColumn);
2870 buffer.append(EOL);
2871
2872 for (final Attribute a : attributes.values())
2873 {
2874 final String name = a.getName();
2875 for (final ASN1OctetString value : a.getRawValues())
2876 {
2877 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2878 buffer.append(EOL);
2879 }
2880 }
2881 }
2882
2883
2884
2885 /**
2886 * Retrieves a string representation of this entry.
2887 *
2888 * @return A string representation of this entry.
2889 */
2890 @Override()
2891 public final String toString()
2892 {
2893 final StringBuilder buffer = new StringBuilder();
2894 toString(buffer);
2895 return buffer.toString();
2896 }
2897
2898
2899
2900 /**
2901 * Appends a string representation of this entry to the provided buffer.
2902 *
2903 * @param buffer The buffer to which to append the string representation of
2904 * this entry.
2905 */
2906 public void toString(final StringBuilder buffer)
2907 {
2908 buffer.append("Entry(dn='");
2909 buffer.append(dn);
2910 buffer.append("', attributes={");
2911
2912 final Iterator<Attribute> iterator = attributes.values().iterator();
2913
2914 while (iterator.hasNext())
2915 {
2916 iterator.next().toString(buffer);
2917 if (iterator.hasNext())
2918 {
2919 buffer.append(", ");
2920 }
2921 }
2922
2923 buffer.append("})");
2924 }
2925 }