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