001 /*
002 * Copyright 2007-2013 UnboundID Corp.
003 * All Rights Reserved.
004 */
005 /*
006 * Copyright (C) 2008-2013 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 * Removes the specified attribute from this entry.
1314 *
1315 * @param attributeName The name of the attribute to remove. It must not be
1316 * {@code null}.
1317 *
1318 * @return {@code true} if the attribute was removed from the entry, or
1319 * {@code false} if it was not present.
1320 */
1321 public boolean removeAttribute(final String attributeName)
1322 {
1323 ensureNotNull(attributeName);
1324
1325 if (schema == null)
1326 {
1327 return (attributes.remove(toLowerCase(attributeName)) != null);
1328 }
1329 else
1330 {
1331 final Attribute a = getAttribute(attributeName, schema);
1332 if (a == null)
1333 {
1334 return false;
1335 }
1336 else
1337 {
1338 attributes.remove(toLowerCase(a.getName()));
1339 return true;
1340 }
1341 }
1342 }
1343
1344
1345
1346 /**
1347 * Removes the specified attribute value from this entry if it is present. If
1348 * it is the last value for the attribute, then the entire attribute will be
1349 * removed. If the specified value is not present, then no change will be
1350 * made.
1351 *
1352 * @param attributeName The name of the attribute from which to remove the
1353 * value. It must not be {@code null}.
1354 * @param attributeValue The value to remove from the attribute. It must
1355 * not be {@code null}.
1356 *
1357 * @return {@code true} if the attribute value was removed from the entry, or
1358 * {@code false} if it was not present.
1359 */
1360 public boolean removeAttributeValue(final String attributeName,
1361 final String attributeValue)
1362 {
1363 return removeAttributeValue(attributeName, attributeValue, null);
1364 }
1365
1366
1367
1368 /**
1369 * Removes the specified attribute value from this entry if it is present. If
1370 * it is the last value for the attribute, then the entire attribute will be
1371 * removed. If the specified value is not present, then no change will be
1372 * made.
1373 *
1374 * @param attributeName The name of the attribute from which to remove the
1375 * value. It must not be {@code null}.
1376 * @param attributeValue The value to remove from the attribute. It must
1377 * not be {@code null}.
1378 * @param matchingRule The matching rule to use for the attribute. It may
1379 * be {@code null} to use the matching rule associated
1380 * with the attribute.
1381 *
1382 * @return {@code true} if the attribute value was removed from the entry, or
1383 * {@code false} if it was not present.
1384 */
1385 public boolean removeAttributeValue(final String attributeName,
1386 final String attributeValue,
1387 final MatchingRule matchingRule)
1388 {
1389 ensureNotNull(attributeName, attributeValue);
1390
1391 final Attribute attr = getAttribute(attributeName, schema);
1392 if (attr == null)
1393 {
1394 return false;
1395 }
1396 else
1397 {
1398 final String lowerName = toLowerCase(attr.getName());
1399 final Attribute newAttr = Attribute.removeValues(attr,
1400 new Attribute(attributeName, attributeValue), matchingRule);
1401 if (newAttr.hasValue())
1402 {
1403 attributes.put(lowerName, newAttr);
1404 }
1405 else
1406 {
1407 attributes.remove(lowerName);
1408 }
1409
1410 return (attr.getRawValues().length != newAttr.getRawValues().length);
1411 }
1412 }
1413
1414
1415
1416 /**
1417 * Removes the specified attribute value from this entry if it is present. If
1418 * it is the last value for the attribute, then the entire attribute will be
1419 * removed. If the specified value is not present, then no change will be
1420 * made.
1421 *
1422 * @param attributeName The name of the attribute from which to remove the
1423 * value. It must not be {@code null}.
1424 * @param attributeValue The value to remove from the attribute. It must
1425 * not be {@code null}.
1426 *
1427 * @return {@code true} if the attribute value was removed from the entry, or
1428 * {@code false} if it was not present.
1429 */
1430 public boolean removeAttributeValue(final String attributeName,
1431 final byte[] attributeValue)
1432 {
1433 return removeAttributeValue(attributeName, attributeValue, null);
1434 }
1435
1436
1437
1438 /**
1439 * Removes the specified attribute value from this entry if it is present. If
1440 * it is the last value for the attribute, then the entire attribute will be
1441 * removed. If the specified value is not present, then no change will be
1442 * made.
1443 *
1444 * @param attributeName The name of the attribute from which to remove the
1445 * value. It must not be {@code null}.
1446 * @param attributeValue The value to remove from the attribute. It must
1447 * not be {@code null}.
1448 * @param matchingRule The matching rule to use for the attribute. It may
1449 * be {@code null} to use the matching rule associated
1450 * with the attribute.
1451 *
1452 * @return {@code true} if the attribute value was removed from the entry, or
1453 * {@code false} if it was not present.
1454 */
1455 public boolean removeAttributeValue(final String attributeName,
1456 final byte[] attributeValue,
1457 final MatchingRule matchingRule)
1458 {
1459 ensureNotNull(attributeName, attributeValue);
1460
1461 final Attribute attr = getAttribute(attributeName, schema);
1462 if (attr == null)
1463 {
1464 return false;
1465 }
1466 else
1467 {
1468 final String lowerName = toLowerCase(attr.getName());
1469 final Attribute newAttr = Attribute.removeValues(attr,
1470 new Attribute(attributeName, attributeValue), matchingRule);
1471 if (newAttr.hasValue())
1472 {
1473 attributes.put(lowerName, newAttr);
1474 }
1475 else
1476 {
1477 attributes.remove(lowerName);
1478 }
1479
1480 return (attr.getRawValues().length != newAttr.getRawValues().length);
1481 }
1482 }
1483
1484
1485
1486 /**
1487 * Removes the specified attribute values from this entry if they are present.
1488 * If the attribute does not have any remaining values, then the entire
1489 * attribute will be removed. If any of the provided values are not present,
1490 * then they will be ignored.
1491 *
1492 * @param attributeName The name of the attribute from which to remove the
1493 * values. It must not be {@code null}.
1494 * @param attributeValues The set of values to remove from the attribute.
1495 * It must not be {@code null}.
1496 *
1497 * @return {@code true} if any attribute values were removed from the entry,
1498 * or {@code false} none of them were present.
1499 */
1500 public boolean removeAttributeValues(final String attributeName,
1501 final String... attributeValues)
1502 {
1503 ensureNotNull(attributeName, attributeValues);
1504
1505 final Attribute attr = getAttribute(attributeName, schema);
1506 if (attr == null)
1507 {
1508 return false;
1509 }
1510 else
1511 {
1512 final String lowerName = toLowerCase(attr.getName());
1513 final Attribute newAttr = Attribute.removeValues(attr,
1514 new Attribute(attributeName, attributeValues));
1515 if (newAttr.hasValue())
1516 {
1517 attributes.put(lowerName, newAttr);
1518 }
1519 else
1520 {
1521 attributes.remove(lowerName);
1522 }
1523
1524 return (attr.getRawValues().length != newAttr.getRawValues().length);
1525 }
1526 }
1527
1528
1529
1530 /**
1531 * Removes the specified attribute values from this entry if they are present.
1532 * If the attribute does not have any remaining values, then the entire
1533 * attribute will be removed. If any of the provided values are not present,
1534 * then they will be ignored.
1535 *
1536 * @param attributeName The name of the attribute from which to remove the
1537 * values. It must not be {@code null}.
1538 * @param attributeValues The set of values to remove from the attribute.
1539 * It must not be {@code null}.
1540 *
1541 * @return {@code true} if any attribute values were removed from the entry,
1542 * or {@code false} none of them were present.
1543 */
1544 public boolean removeAttributeValues(final String attributeName,
1545 final byte[]... attributeValues)
1546 {
1547 ensureNotNull(attributeName, attributeValues);
1548
1549 final Attribute attr = getAttribute(attributeName, schema);
1550 if (attr == null)
1551 {
1552 return false;
1553 }
1554 else
1555 {
1556 final String lowerName = toLowerCase(attr.getName());
1557 final Attribute newAttr = Attribute.removeValues(attr,
1558 new Attribute(attributeName, attributeValues));
1559 if (newAttr.hasValue())
1560 {
1561 attributes.put(lowerName, newAttr);
1562 }
1563 else
1564 {
1565 attributes.remove(lowerName);
1566 }
1567
1568 return (attr.getRawValues().length != newAttr.getRawValues().length);
1569 }
1570 }
1571
1572
1573
1574 /**
1575 * Adds the provided attribute to this entry, replacing any existing set of
1576 * values for the associated attribute.
1577 *
1578 * @param attribute The attribute to be included in this entry. It must not
1579 * be {@code null}.
1580 */
1581 public void setAttribute(final Attribute attribute)
1582 {
1583 ensureNotNull(attribute);
1584
1585 final String lowerName;
1586 final Attribute a = getAttribute(attribute.getName(), schema);
1587 if (a == null)
1588 {
1589 lowerName = toLowerCase(attribute.getName());
1590 }
1591 else
1592 {
1593 lowerName = toLowerCase(a.getName());
1594 }
1595
1596 attributes.put(lowerName, attribute);
1597 }
1598
1599
1600
1601 /**
1602 * Adds the provided attribute to this entry, replacing any existing set of
1603 * values for the associated attribute.
1604 *
1605 * @param attributeName The name to use for the attribute. It must not be
1606 * {@code null}.
1607 * @param attributeValue The value to use for the attribute. It must not be
1608 * {@code null}.
1609 */
1610 public void setAttribute(final String attributeName,
1611 final String attributeValue)
1612 {
1613 ensureNotNull(attributeName, attributeValue);
1614 setAttribute(new Attribute(attributeName, schema, attributeValue));
1615 }
1616
1617
1618
1619 /**
1620 * Adds the provided attribute to this entry, replacing any existing set of
1621 * values for the associated attribute.
1622 *
1623 * @param attributeName The name to use for the attribute. It must not be
1624 * {@code null}.
1625 * @param attributeValue The value to use for the attribute. It must not be
1626 * {@code null}.
1627 */
1628 public void setAttribute(final String attributeName,
1629 final byte[] attributeValue)
1630 {
1631 ensureNotNull(attributeName, attributeValue);
1632 setAttribute(new Attribute(attributeName, schema, attributeValue));
1633 }
1634
1635
1636
1637 /**
1638 * Adds the provided attribute to this entry, replacing any existing set of
1639 * values for the associated attribute.
1640 *
1641 * @param attributeName The name to use for the attribute. It must not be
1642 * {@code null}.
1643 * @param attributeValues The set of values to use for the attribute. It
1644 * must not be {@code null}.
1645 */
1646 public void setAttribute(final String attributeName,
1647 final String... attributeValues)
1648 {
1649 ensureNotNull(attributeName, attributeValues);
1650 setAttribute(new Attribute(attributeName, schema, attributeValues));
1651 }
1652
1653
1654
1655 /**
1656 * Adds the provided attribute to this entry, replacing any existing set of
1657 * values for the associated attribute.
1658 *
1659 * @param attributeName The name to use for the attribute. It must not be
1660 * {@code null}.
1661 * @param attributeValues The set of values to use for the attribute. It
1662 * must not be {@code null}.
1663 */
1664 public void setAttribute(final String attributeName,
1665 final byte[]... attributeValues)
1666 {
1667 ensureNotNull(attributeName, attributeValues);
1668 setAttribute(new Attribute(attributeName, schema, attributeValues));
1669 }
1670
1671
1672
1673 /**
1674 * Indicates whether this entry falls within the range of the provided search
1675 * base DN and scope.
1676 *
1677 * @param baseDN The base DN for which to make the determination. It must
1678 * not be {@code null}.
1679 * @param scope The scope for which to make the determination. It must not
1680 * be {@code null}.
1681 *
1682 * @return {@code true} if this entry is within the range of the provided
1683 * base and scope, or {@code false} if not.
1684 *
1685 * @throws LDAPException If a problem occurs while making the determination.
1686 */
1687 public boolean matchesBaseAndScope(final String baseDN,
1688 final SearchScope scope)
1689 throws LDAPException
1690 {
1691 return getParsedDN().matchesBaseAndScope(new DN(baseDN), scope);
1692 }
1693
1694
1695
1696 /**
1697 * Indicates whether this entry falls within the range of the provided search
1698 * base DN and scope.
1699 *
1700 * @param baseDN The base DN for which to make the determination. It must
1701 * not be {@code null}.
1702 * @param scope The scope for which to make the determination. It must not
1703 * be {@code null}.
1704 *
1705 * @return {@code true} if this entry is within the range of the provided
1706 * base and scope, or {@code false} if not.
1707 *
1708 * @throws LDAPException If a problem occurs while making the determination.
1709 */
1710 public boolean matchesBaseAndScope(final DN baseDN, final SearchScope scope)
1711 throws LDAPException
1712 {
1713 return getParsedDN().matchesBaseAndScope(baseDN, scope);
1714 }
1715
1716
1717
1718 /**
1719 * Retrieves a set of modifications that can be applied to the source entry in
1720 * order to make it match the target entry. The diff will be generated in
1721 * reversible form (i.e., the same as calling
1722 * {@code diff(sourceEntry, targetEntry, ignoreRDN, true, attributes)}.
1723 *
1724 * @param sourceEntry The source entry for which the set of modifications
1725 * should be generated.
1726 * @param targetEntry The target entry, which is what the source entry
1727 * should look like if the returned modifications are
1728 * applied.
1729 * @param ignoreRDN Indicates whether to ignore differences in the RDNs
1730 * of the provided entries. If this is {@code false},
1731 * then the resulting set of modifications may include
1732 * changes to the RDN attribute. If it is {@code true},
1733 * then differences in the entry DNs will be ignored.
1734 * @param attributes The set of attributes to be compared. If this is
1735 * {@code null} or empty, then all attributes will be
1736 * compared.
1737 *
1738 * @return A set of modifications that can be applied to the source entry in
1739 * order to make it match the target entry.
1740 */
1741 public static List<Modification> diff(final Entry sourceEntry,
1742 final Entry targetEntry,
1743 final boolean ignoreRDN,
1744 final String... attributes)
1745 {
1746 return diff(sourceEntry, targetEntry, ignoreRDN, true, attributes);
1747 }
1748
1749
1750
1751 /**
1752 * Retrieves a set of modifications that can be applied to the source entry in
1753 * order to make it match the target entry.
1754 *
1755 * @param sourceEntry The source entry for which the set of modifications
1756 * should be generated.
1757 * @param targetEntry The target entry, which is what the source entry
1758 * should look like if the returned modifications are
1759 * applied.
1760 * @param ignoreRDN Indicates whether to ignore differences in the RDNs
1761 * of the provided entries. If this is {@code false},
1762 * then the resulting set of modifications may include
1763 * changes to the RDN attribute. If it is {@code true},
1764 * then differences in the entry DNs will be ignored.
1765 * @param reversible Indicates whether to generate the diff in reversible
1766 * form. In reversible form, only the ADD or DELETE
1767 * modification types will be used so that source entry
1768 * could be reconstructed from the target and the
1769 * resulting modifications. In non-reversible form, only
1770 * the REPLACE modification type will be used. Attempts
1771 * to apply the modifications obtained when using
1772 * reversible form are more likely to fail if the entry
1773 * has been modified since the source and target forms
1774 * were obtained.
1775 * @param attributes The set of attributes to be compared. If this is
1776 * {@code null} or empty, then all attributes will be
1777 * compared.
1778 *
1779 * @return A set of modifications that can be applied to the source entry in
1780 * order to make it match the target entry.
1781 */
1782 public static List<Modification> diff(final Entry sourceEntry,
1783 final Entry targetEntry,
1784 final boolean ignoreRDN,
1785 final boolean reversible,
1786 final String... attributes)
1787 {
1788 HashSet<String> compareAttrs = null;
1789 if ((attributes != null) && (attributes.length > 0))
1790 {
1791 compareAttrs = new HashSet<String>(attributes.length);
1792 for (final String s : attributes)
1793 {
1794 compareAttrs.add(toLowerCase(s));
1795 }
1796 }
1797
1798 final LinkedHashMap<String,Attribute> sourceOnlyAttrs =
1799 new LinkedHashMap<String,Attribute>();
1800 final LinkedHashMap<String,Attribute> targetOnlyAttrs =
1801 new LinkedHashMap<String,Attribute>();
1802 final LinkedHashMap<String,Attribute> commonAttrs =
1803 new LinkedHashMap<String,Attribute>();
1804
1805 for (final Map.Entry<String,Attribute> e :
1806 sourceEntry.attributes.entrySet())
1807 {
1808 final String lowerName = toLowerCase(e.getKey());
1809 if ((compareAttrs != null) && (! compareAttrs.contains(lowerName)))
1810 {
1811 continue;
1812 }
1813
1814 sourceOnlyAttrs.put(lowerName, e.getValue());
1815 commonAttrs.put(lowerName, e.getValue());
1816 }
1817
1818 for (final Map.Entry<String,Attribute> e :
1819 targetEntry.attributes.entrySet())
1820 {
1821 final String lowerName = toLowerCase(e.getKey());
1822 if ((compareAttrs != null) && (! compareAttrs.contains(lowerName)))
1823 {
1824 continue;
1825 }
1826
1827
1828 if (sourceOnlyAttrs.remove(lowerName) == null)
1829 {
1830 // It wasn't in the set of source attributes, so it must be a
1831 // target-only attribute.
1832 targetOnlyAttrs.put(lowerName,e.getValue());
1833 }
1834 }
1835
1836 for (final String lowerName : sourceOnlyAttrs.keySet())
1837 {
1838 commonAttrs.remove(lowerName);
1839 }
1840
1841 RDN sourceRDN = null;
1842 RDN targetRDN = null;
1843 if (ignoreRDN)
1844 {
1845 try
1846 {
1847 sourceRDN = sourceEntry.getRDN();
1848 }
1849 catch (Exception e)
1850 {
1851 debugException(e);
1852 }
1853
1854 try
1855 {
1856 targetRDN = targetEntry.getRDN();
1857 }
1858 catch (Exception e)
1859 {
1860 debugException(e);
1861 }
1862 }
1863
1864 final ArrayList<Modification> mods = new ArrayList<Modification>(10);
1865
1866 for (final Attribute a : sourceOnlyAttrs.values())
1867 {
1868 if (reversible)
1869 {
1870 ASN1OctetString[] values = a.getRawValues();
1871 if ((sourceRDN != null) && (sourceRDN.hasAttribute(a.getName())))
1872 {
1873 final ArrayList<ASN1OctetString> newValues =
1874 new ArrayList<ASN1OctetString>(values.length);
1875 for (final ASN1OctetString value : values)
1876 {
1877 if (! sourceRDN.hasAttributeValue(a.getName(), value.getValue()))
1878 {
1879 newValues.add(value);
1880 }
1881 }
1882
1883 if (newValues.isEmpty())
1884 {
1885 continue;
1886 }
1887 else
1888 {
1889 values = new ASN1OctetString[newValues.size()];
1890 newValues.toArray(values);
1891 }
1892 }
1893
1894 mods.add(new Modification(ModificationType.DELETE, a.getName(),
1895 values));
1896 }
1897 else
1898 {
1899 mods.add(new Modification(ModificationType.REPLACE, a.getName()));
1900 }
1901 }
1902
1903 for (final Attribute a : targetOnlyAttrs.values())
1904 {
1905 ASN1OctetString[] values = a.getRawValues();
1906 if ((targetRDN != null) && (targetRDN.hasAttribute(a.getName())))
1907 {
1908 final ArrayList<ASN1OctetString> newValues =
1909 new ArrayList<ASN1OctetString>(values.length);
1910 for (final ASN1OctetString value : values)
1911 {
1912 if (! targetRDN.hasAttributeValue(a.getName(), value.getValue()))
1913 {
1914 newValues.add(value);
1915 }
1916 }
1917
1918 if (newValues.isEmpty())
1919 {
1920 continue;
1921 }
1922 else
1923 {
1924 values = new ASN1OctetString[newValues.size()];
1925 newValues.toArray(values);
1926 }
1927 }
1928
1929 if (reversible)
1930 {
1931 mods.add(new Modification(ModificationType.ADD, a.getName(), values));
1932 }
1933 else
1934 {
1935 mods.add(new Modification(ModificationType.REPLACE, a.getName(),
1936 values));
1937 }
1938 }
1939
1940 for (final Attribute sourceAttr : commonAttrs.values())
1941 {
1942 final Attribute targetAttr =
1943 targetEntry.getAttribute(sourceAttr.getName());
1944 if (sourceAttr.equals(targetAttr))
1945 {
1946 continue;
1947 }
1948
1949 if (reversible ||
1950 ((targetRDN != null) && targetRDN.hasAttribute(targetAttr.getName())))
1951 {
1952 final ASN1OctetString[] sourceValueArray = sourceAttr.getRawValues();
1953 final LinkedHashMap<ASN1OctetString,ASN1OctetString> sourceValues =
1954 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
1955 sourceValueArray.length);
1956 for (final ASN1OctetString s : sourceValueArray)
1957 {
1958 try
1959 {
1960 sourceValues.put(sourceAttr.getMatchingRule().normalize(s), s);
1961 }
1962 catch (final Exception e)
1963 {
1964 debugException(e);
1965 sourceValues.put(s, s);
1966 }
1967 }
1968
1969 final ASN1OctetString[] targetValueArray = targetAttr.getRawValues();
1970 final LinkedHashMap<ASN1OctetString,ASN1OctetString> targetValues =
1971 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
1972 targetValueArray.length);
1973 for (final ASN1OctetString s : targetValueArray)
1974 {
1975 try
1976 {
1977 targetValues.put(sourceAttr.getMatchingRule().normalize(s), s);
1978 }
1979 catch (final Exception e)
1980 {
1981 debugException(e);
1982 targetValues.put(s, s);
1983 }
1984 }
1985
1986 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
1987 sourceIterator = sourceValues.entrySet().iterator();
1988 while (sourceIterator.hasNext())
1989 {
1990 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
1991 sourceIterator.next();
1992 if (targetValues.remove(e.getKey()) != null)
1993 {
1994 sourceIterator.remove();
1995 }
1996 else if ((sourceRDN != null) &&
1997 sourceRDN.hasAttributeValue(sourceAttr.getName(),
1998 e.getValue().getValue()))
1999 {
2000 sourceIterator.remove();
2001 }
2002 }
2003
2004 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
2005 targetIterator = targetValues.entrySet().iterator();
2006 while (targetIterator.hasNext())
2007 {
2008 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
2009 targetIterator.next();
2010 if ((targetRDN != null) &&
2011 targetRDN.hasAttributeValue(targetAttr.getName(),
2012 e.getValue().getValue()))
2013 {
2014 targetIterator.remove();
2015 }
2016 }
2017
2018 final ArrayList<ASN1OctetString> addValues =
2019 new ArrayList<ASN1OctetString>(targetValues.values());
2020 final ArrayList<ASN1OctetString> delValues =
2021 new ArrayList<ASN1OctetString>(sourceValues.values());
2022
2023 if (! addValues.isEmpty())
2024 {
2025 final ASN1OctetString[] addArray =
2026 new ASN1OctetString[addValues.size()];
2027 mods.add(new Modification(ModificationType.ADD, targetAttr.getName(),
2028 addValues.toArray(addArray)));
2029 }
2030
2031 if (! delValues.isEmpty())
2032 {
2033 final ASN1OctetString[] delArray =
2034 new ASN1OctetString[delValues.size()];
2035 mods.add(new Modification(ModificationType.DELETE,
2036 sourceAttr.getName(), delValues.toArray(delArray)));
2037 }
2038 }
2039 else
2040 {
2041 mods.add(new Modification(ModificationType.REPLACE,
2042 targetAttr.getName(), targetAttr.getRawValues()));
2043 }
2044 }
2045
2046 return mods;
2047 }
2048
2049
2050
2051 /**
2052 * Merges the contents of all provided entries so that the resulting entry
2053 * will contain all attribute values present in at least one of the entries.
2054 *
2055 * @param entries The set of entries to be merged. At least one entry must
2056 * be provided.
2057 *
2058 * @return An entry containing all attribute values present in at least one
2059 * of the entries.
2060 */
2061 public static Entry mergeEntries(final Entry... entries)
2062 {
2063 ensureNotNull(entries);
2064 ensureTrue(entries.length > 0);
2065
2066 final Entry newEntry = entries[0].duplicate();
2067
2068 for (int i=1; i < entries.length; i++)
2069 {
2070 for (final Attribute a : entries[i].attributes.values())
2071 {
2072 newEntry.addAttribute(a);
2073 }
2074 }
2075
2076 return newEntry;
2077 }
2078
2079
2080
2081 /**
2082 * Intersects the contents of all provided entries so that the resulting
2083 * entry will contain only attribute values present in all of the provided
2084 * entries.
2085 *
2086 * @param entries The set of entries to be intersected. At least one entry
2087 * must be provided.
2088 *
2089 * @return An entry containing only attribute values contained in all of the
2090 * provided entries.
2091 */
2092 public static Entry intersectEntries(final Entry... entries)
2093 {
2094 ensureNotNull(entries);
2095 ensureTrue(entries.length > 0);
2096
2097 final Entry newEntry = entries[0].duplicate();
2098
2099 for (final Attribute a : entries[0].attributes.values())
2100 {
2101 final String name = a.getName();
2102 for (final byte[] v : a.getValueByteArrays())
2103 {
2104 for (int i=1; i < entries.length; i++)
2105 {
2106 if (! entries[i].hasAttributeValue(name, v))
2107 {
2108 newEntry.removeAttributeValue(name, v);
2109 break;
2110 }
2111 }
2112 }
2113 }
2114
2115 return newEntry;
2116 }
2117
2118
2119
2120 /**
2121 * Creates a duplicate of the provided entry with the given set of
2122 * modifications applied to it.
2123 *
2124 * @param entry The entry to be modified. It must not be
2125 * {@code null}.
2126 * @param lenient Indicates whether to exhibit a lenient behavior for
2127 * the modifications, which will cause it to ignore
2128 * problems like trying to add values that already
2129 * exist or to remove nonexistent attributes or values.
2130 * @param modifications The set of modifications to apply to the entry. It
2131 * must not be {@code null} or empty.
2132 *
2133 * @return An updated version of the entry with the requested modifications
2134 * applied.
2135 *
2136 * @throws LDAPException If a problem occurs while attempting to apply the
2137 * modifications.
2138 */
2139 public static Entry applyModifications(final Entry entry,
2140 final boolean lenient,
2141 final Modification... modifications)
2142 throws LDAPException
2143 {
2144 ensureNotNull(entry, modifications);
2145 ensureFalse(modifications.length == 0);
2146
2147 return applyModifications(entry, lenient, Arrays.asList(modifications));
2148 }
2149
2150
2151
2152 /**
2153 * Creates a duplicate of the provided entry with the given set of
2154 * modifications applied to it.
2155 *
2156 * @param entry The entry to be modified. It must not be
2157 * {@code null}.
2158 * @param lenient Indicates whether to exhibit a lenient behavior for
2159 * the modifications, which will cause it to ignore
2160 * problems like trying to add values that already
2161 * exist or to remove nonexistent attributes or values.
2162 * @param modifications The set of modifications to apply to the entry. It
2163 * must not be {@code null} or empty.
2164 *
2165 * @return An updated version of the entry with the requested modifications
2166 * applied.
2167 *
2168 * @throws LDAPException If a problem occurs while attempting to apply the
2169 * modifications.
2170 */
2171 public static Entry applyModifications(final Entry entry,
2172 final boolean lenient,
2173 final List<Modification> modifications)
2174 throws LDAPException
2175 {
2176 ensureNotNull(entry, modifications);
2177 ensureFalse(modifications.isEmpty());
2178
2179 final Entry e = entry.duplicate();
2180 final ArrayList<String> errors =
2181 new ArrayList<String>(modifications.size());
2182 ResultCode resultCode = null;
2183
2184 // Get the RDN for the entry to ensure that RDN modifications are not
2185 // allowed.
2186 RDN rdn = null;
2187 try
2188 {
2189 rdn = entry.getRDN();
2190 }
2191 catch (final LDAPException le)
2192 {
2193 debugException(le);
2194 }
2195
2196 for (final Modification m : modifications)
2197 {
2198 final String name = m.getAttributeName();
2199 final byte[][] values = m.getValueByteArrays();
2200 switch (m.getModificationType().intValue())
2201 {
2202 case ModificationType.ADD_INT_VALUE:
2203 if (lenient)
2204 {
2205 e.addAttribute(m.getAttribute());
2206 }
2207 else
2208 {
2209 if (values.length == 0)
2210 {
2211 errors.add(ERR_ENTRY_APPLY_MODS_ADD_NO_VALUES.get(name));
2212 }
2213
2214 for (int i=0; i < values.length; i++)
2215 {
2216 if (! e.addAttribute(name, values[i]))
2217 {
2218 if (resultCode == null)
2219 {
2220 resultCode = ResultCode.ATTRIBUTE_OR_VALUE_EXISTS;
2221 }
2222 errors.add(ERR_ENTRY_APPLY_MODS_ADD_EXISTING.get(
2223 m.getValues()[i], name));
2224 }
2225 }
2226 }
2227 break;
2228
2229 case ModificationType.DELETE_INT_VALUE:
2230 if (values.length == 0)
2231 {
2232 if ((rdn != null) && rdn.hasAttribute(name))
2233 {
2234 final String msg =
2235 ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN());
2236 if (! errors.contains(msg))
2237 {
2238 errors.add(msg);
2239 }
2240
2241 if (resultCode == null)
2242 {
2243 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2244 }
2245 break;
2246 }
2247
2248 final boolean removed = e.removeAttribute(name);
2249 if (! (lenient || removed))
2250 {
2251 if (resultCode == null)
2252 {
2253 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2254 }
2255 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_ATTR.get(
2256 name));
2257 }
2258 }
2259 else
2260 {
2261 deleteValueLoop:
2262 for (int i=0; i < values.length; i++)
2263 {
2264 if ((rdn != null) && rdn.hasAttributeValue(name, values[i]))
2265 {
2266 final String msg =
2267 ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN());
2268 if (! errors.contains(msg))
2269 {
2270 errors.add(msg);
2271 }
2272
2273 if (resultCode == null)
2274 {
2275 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2276 }
2277 break deleteValueLoop;
2278 }
2279
2280 final boolean removed = e.removeAttributeValue(name, values[i]);
2281 if (! (lenient || removed))
2282 {
2283 if (resultCode == null)
2284 {
2285 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2286 }
2287 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_VALUE.get(
2288 m.getValues()[i], name));
2289 }
2290 }
2291 }
2292 break;
2293
2294 case ModificationType.REPLACE_INT_VALUE:
2295 if ((rdn != null) && rdn.hasAttribute(name))
2296 {
2297 final String msg =
2298 ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN());
2299 if (! errors.contains(msg))
2300 {
2301 errors.add(msg);
2302 }
2303
2304 if (resultCode == null)
2305 {
2306 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2307 }
2308 continue;
2309 }
2310
2311 if (values.length == 0)
2312 {
2313 e.removeAttribute(name);
2314 }
2315 else
2316 {
2317 e.setAttribute(m.getAttribute());
2318 }
2319 break;
2320
2321 case ModificationType.INCREMENT_INT_VALUE:
2322 final Attribute a = e.getAttribute(name);
2323 if ((a == null) || (! a.hasValue()))
2324 {
2325 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_SUCH_ATTR.get(name));
2326 continue;
2327 }
2328
2329 if (a.size() > 1)
2330 {
2331 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NOT_SINGLE_VALUED.get(
2332 name));
2333 continue;
2334 }
2335
2336 if ((rdn != null) && rdn.hasAttribute(name))
2337 {
2338 final String msg =
2339 ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN());
2340 if (! errors.contains(msg))
2341 {
2342 errors.add(msg);
2343 }
2344
2345 if (resultCode == null)
2346 {
2347 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2348 }
2349 continue;
2350 }
2351
2352 final BigInteger currentValue;
2353 try
2354 {
2355 currentValue = new BigInteger(a.getValue());
2356 }
2357 catch (NumberFormatException nfe)
2358 {
2359 debugException(nfe);
2360 errors.add(
2361 ERR_ENTRY_APPLY_MODS_INCREMENT_ENTRY_VALUE_NOT_INTEGER.get(
2362 name, a.getValue()));
2363 continue;
2364 }
2365
2366 if (values.length == 0)
2367 {
2368 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_MOD_VALUES.get(name));
2369 continue;
2370 }
2371 else if (values.length > 1)
2372 {
2373 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MULTIPLE_MOD_VALUES.get(
2374 name));
2375 continue;
2376 }
2377
2378 final BigInteger incrementValue;
2379 final String incrementValueStr = m.getValues()[0];
2380 try
2381 {
2382 incrementValue = new BigInteger(incrementValueStr);
2383 }
2384 catch (NumberFormatException nfe)
2385 {
2386 debugException(nfe);
2387 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MOD_VALUE_NOT_INTEGER.get(
2388 name, incrementValueStr));
2389 continue;
2390 }
2391
2392 final BigInteger newValue = currentValue.add(incrementValue);
2393 e.setAttribute(name, newValue.toString());
2394 break;
2395
2396 default:
2397 errors.add(ERR_ENTRY_APPLY_MODS_UNKNOWN_TYPE.get(
2398 String.valueOf(m.getModificationType())));
2399 break;
2400 }
2401 }
2402
2403 if (errors.isEmpty())
2404 {
2405 return e;
2406 }
2407
2408 if (resultCode == null)
2409 {
2410 resultCode = ResultCode.CONSTRAINT_VIOLATION;
2411 }
2412
2413 throw new LDAPException(resultCode,
2414 ERR_ENTRY_APPLY_MODS_FAILURE.get(e.getDN(),
2415 concatenateStrings(errors)));
2416 }
2417
2418
2419
2420 /**
2421 * Generates a hash code for this entry.
2422 *
2423 * @return The generated hash code for this entry.
2424 */
2425 @Override()
2426 public int hashCode()
2427 {
2428 int hashCode = 0;
2429 try
2430 {
2431 hashCode += getParsedDN().hashCode();
2432 }
2433 catch (LDAPException le)
2434 {
2435 debugException(le);
2436 hashCode += dn.hashCode();
2437 }
2438
2439 for (final Attribute a : attributes.values())
2440 {
2441 hashCode += a.hashCode();
2442 }
2443
2444 return hashCode;
2445 }
2446
2447
2448
2449 /**
2450 * Indicates whether the provided object is equal to this entry. The provided
2451 * object will only be considered equal to this entry if it is an entry with
2452 * the same DN and set of attributes.
2453 *
2454 * @param o The object for which to make the determination.
2455 *
2456 * @return {@code true} if the provided object is considered equal to this
2457 * entry, or {@code false} if not.
2458 */
2459 @Override()
2460 public boolean equals(final Object o)
2461 {
2462 if (o == null)
2463 {
2464 return false;
2465 }
2466
2467 if (o == this)
2468 {
2469 return true;
2470 }
2471
2472 if (! (o instanceof Entry))
2473 {
2474 return false;
2475 }
2476
2477 final Entry e = (Entry) o;
2478
2479 try
2480 {
2481 final DN thisDN = getParsedDN();
2482 final DN thatDN = e.getParsedDN();
2483 if (! thisDN.equals(thatDN))
2484 {
2485 return false;
2486 }
2487 }
2488 catch (LDAPException le)
2489 {
2490 debugException(le);
2491 if (! dn.equals(e.dn))
2492 {
2493 return false;
2494 }
2495 }
2496
2497 if (attributes.size() != e.attributes.size())
2498 {
2499 return false;
2500 }
2501
2502 for (final Attribute a : attributes.values())
2503 {
2504 if (! e.hasAttribute(a))
2505 {
2506 return false;
2507 }
2508 }
2509
2510 return true;
2511 }
2512
2513
2514
2515 /**
2516 * Creates a new entry that is a duplicate of this entry.
2517 *
2518 * @return A new entry that is a duplicate of this entry.
2519 */
2520 public Entry duplicate()
2521 {
2522 return new Entry(dn, schema, attributes.values());
2523 }
2524
2525
2526
2527 /**
2528 * Retrieves an LDIF representation of this entry, with each attribute value
2529 * on a separate line. Long lines will not be wrapped.
2530 *
2531 * @return An LDIF representation of this entry.
2532 */
2533 public final String[] toLDIF()
2534 {
2535 return toLDIF(0);
2536 }
2537
2538
2539
2540 /**
2541 * Retrieves an LDIF representation of this entry, with each attribute value
2542 * on a separate line. Long lines will be wrapped at the specified column.
2543 *
2544 * @param wrapColumn The column at which long lines should be wrapped. A
2545 * value less than or equal to two indicates that no
2546 * wrapping should be performed.
2547 *
2548 * @return An LDIF representation of this entry.
2549 */
2550 public final String[] toLDIF(final int wrapColumn)
2551 {
2552 List<String> ldifLines = new ArrayList<String>(2*attributes.size());
2553 ldifLines.add(LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn)));
2554
2555 for (final Attribute a : attributes.values())
2556 {
2557 final String name = a.getName();
2558 for (final ASN1OctetString value : a.getRawValues())
2559 {
2560 ldifLines.add(LDIFWriter.encodeNameAndValue(name, value));
2561 }
2562 }
2563
2564 if (wrapColumn > 2)
2565 {
2566 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines);
2567 }
2568
2569 final String[] lineArray = new String[ldifLines.size()];
2570 ldifLines.toArray(lineArray);
2571 return lineArray;
2572 }
2573
2574
2575
2576 /**
2577 * Appends an LDIF representation of this entry to the provided buffer. Long
2578 * lines will not be wrapped.
2579 *
2580 * @param buffer The buffer to which the LDIF representation of this entry
2581 * should be written.
2582 */
2583 public final void toLDIF(final ByteStringBuffer buffer)
2584 {
2585 toLDIF(buffer, 0);
2586 }
2587
2588
2589
2590 /**
2591 * Appends an LDIF representation of this entry to the provided buffer.
2592 *
2593 * @param buffer The buffer to which the LDIF representation of this
2594 * entry should be written.
2595 * @param wrapColumn The column at which long lines should be wrapped. A
2596 * value less than or equal to two indicates that no
2597 * wrapping should be performed.
2598 */
2599 public final void toLDIF(final ByteStringBuffer buffer, final int wrapColumn)
2600 {
2601 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2602 wrapColumn);
2603 buffer.append(EOL_BYTES);
2604
2605 for (final Attribute a : attributes.values())
2606 {
2607 final String name = a.getName();
2608 for (final ASN1OctetString value : a.getRawValues())
2609 {
2610 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2611 buffer.append(EOL_BYTES);
2612 }
2613 }
2614 }
2615
2616
2617
2618 /**
2619 * Retrieves an LDIF-formatted string representation of this entry. No
2620 * wrapping will be performed, and no extra blank lines will be added.
2621 *
2622 * @return An LDIF-formatted string representation of this entry.
2623 */
2624 public final String toLDIFString()
2625 {
2626 final StringBuilder buffer = new StringBuilder();
2627 toLDIFString(buffer, 0);
2628 return buffer.toString();
2629 }
2630
2631
2632
2633 /**
2634 * Retrieves an LDIF-formatted string representation of this entry. No
2635 * extra blank lines will be added.
2636 *
2637 * @param wrapColumn The column at which long lines should be wrapped. A
2638 * value less than or equal to two indicates that no
2639 * wrapping should be performed.
2640 *
2641 * @return An LDIF-formatted string representation of this entry.
2642 */
2643 public final String toLDIFString(final int wrapColumn)
2644 {
2645 final StringBuilder buffer = new StringBuilder();
2646 toLDIFString(buffer, wrapColumn);
2647 return buffer.toString();
2648 }
2649
2650
2651
2652 /**
2653 * Appends an LDIF-formatted string representation of this entry to the
2654 * provided buffer. No wrapping will be performed, and no extra blank lines
2655 * will be added.
2656 *
2657 * @param buffer The buffer to which to append the LDIF representation of
2658 * this entry.
2659 */
2660 public final void toLDIFString(final StringBuilder buffer)
2661 {
2662 toLDIFString(buffer, 0);
2663 }
2664
2665
2666
2667 /**
2668 * Appends an LDIF-formatted string representation of this entry to the
2669 * provided buffer. No extra blank lines will be added.
2670 *
2671 * @param buffer The buffer to which to append the LDIF representation
2672 * of this entry.
2673 * @param wrapColumn The column at which long lines should be wrapped. A
2674 * value less than or equal to two indicates that no
2675 * wrapping should be performed.
2676 */
2677 public final void toLDIFString(final StringBuilder buffer,
2678 final int wrapColumn)
2679 {
2680 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2681 wrapColumn);
2682 buffer.append(EOL);
2683
2684 for (final Attribute a : attributes.values())
2685 {
2686 final String name = a.getName();
2687 for (final ASN1OctetString value : a.getRawValues())
2688 {
2689 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2690 buffer.append(EOL);
2691 }
2692 }
2693 }
2694
2695
2696
2697 /**
2698 * Retrieves a string representation of this entry.
2699 *
2700 * @return A string representation of this entry.
2701 */
2702 @Override()
2703 public final String toString()
2704 {
2705 final StringBuilder buffer = new StringBuilder();
2706 toString(buffer);
2707 return buffer.toString();
2708 }
2709
2710
2711
2712 /**
2713 * Appends a string representation of this entry to the provided buffer.
2714 *
2715 * @param buffer The buffer to which to append the string representation of
2716 * this entry.
2717 */
2718 public void toString(final StringBuilder buffer)
2719 {
2720 buffer.append("Entry(dn='");
2721 buffer.append(dn);
2722 buffer.append("', attributes={");
2723
2724 final Iterator<Attribute> iterator = attributes.values().iterator();
2725
2726 while (iterator.hasNext())
2727 {
2728 iterator.next().toString(buffer);
2729 if (iterator.hasNext())
2730 {
2731 buffer.append(", ");
2732 }
2733 }
2734
2735 buffer.append("})");
2736 }
2737 }