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