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. Note that if a list of attributes is
1776 * specified, then matching will be performed only
1777 * against the attribute base name and any differences in
1778 * attribute options will be ignored.
1779 *
1780 * @return A set of modifications that can be applied to the source entry in
1781 * order to make it match the target entry.
1782 */
1783 public static List<Modification> diff(final Entry sourceEntry,
1784 final Entry targetEntry,
1785 final boolean ignoreRDN,
1786 final String... attributes)
1787 {
1788 return diff(sourceEntry, targetEntry, ignoreRDN, true, attributes);
1789 }
1790
1791
1792
1793 /**
1794 * Retrieves a set of modifications that can be applied to the source entry in
1795 * order to make it match the target entry.
1796 *
1797 * @param sourceEntry The source entry for which the set of modifications
1798 * should be generated.
1799 * @param targetEntry The target entry, which is what the source entry
1800 * should look like if the returned modifications are
1801 * applied.
1802 * @param ignoreRDN Indicates whether to ignore differences in the RDNs
1803 * of the provided entries. If this is {@code false},
1804 * then the resulting set of modifications may include
1805 * changes to the RDN attribute. If it is {@code true},
1806 * then differences in the entry DNs will be ignored.
1807 * @param reversible Indicates whether to generate the diff in reversible
1808 * form. In reversible form, only the ADD or DELETE
1809 * modification types will be used so that source entry
1810 * could be reconstructed from the target and the
1811 * resulting modifications. In non-reversible form, only
1812 * the REPLACE modification type will be used. Attempts
1813 * to apply the modifications obtained when using
1814 * reversible form are more likely to fail if the entry
1815 * has been modified since the source and target forms
1816 * were obtained.
1817 * @param attributes The set of attributes to be compared. If this is
1818 * {@code null} or empty, then all attributes will be
1819 * compared. Note that if a list of attributes is
1820 * specified, then matching will be performed only
1821 * against the attribute base name and any differences in
1822 * attribute options will be ignored.
1823 *
1824 * @return A set of modifications that can be applied to the source entry in
1825 * order to make it match the target entry.
1826 */
1827 public static List<Modification> diff(final Entry sourceEntry,
1828 final Entry targetEntry,
1829 final boolean ignoreRDN,
1830 final boolean reversible,
1831 final String... attributes)
1832 {
1833 HashSet<String> compareAttrs = null;
1834 if ((attributes != null) && (attributes.length > 0))
1835 {
1836 compareAttrs = new HashSet<String>(attributes.length);
1837 for (final String s : attributes)
1838 {
1839 compareAttrs.add(toLowerCase(Attribute.getBaseName(s)));
1840 }
1841 }
1842
1843 final LinkedHashMap<String,Attribute> sourceOnlyAttrs =
1844 new LinkedHashMap<String,Attribute>();
1845 final LinkedHashMap<String,Attribute> targetOnlyAttrs =
1846 new LinkedHashMap<String,Attribute>();
1847 final LinkedHashMap<String,Attribute> commonAttrs =
1848 new LinkedHashMap<String,Attribute>();
1849
1850 for (final Map.Entry<String,Attribute> e :
1851 sourceEntry.attributes.entrySet())
1852 {
1853 final String lowerName = toLowerCase(e.getKey());
1854 if ((compareAttrs != null) &&
1855 (! compareAttrs.contains(Attribute.getBaseName(lowerName))))
1856 {
1857 continue;
1858 }
1859
1860 sourceOnlyAttrs.put(lowerName, e.getValue());
1861 commonAttrs.put(lowerName, e.getValue());
1862 }
1863
1864 for (final Map.Entry<String,Attribute> e :
1865 targetEntry.attributes.entrySet())
1866 {
1867 final String lowerName = toLowerCase(e.getKey());
1868 if ((compareAttrs != null) &&
1869 (! compareAttrs.contains(Attribute.getBaseName(lowerName))))
1870 {
1871 continue;
1872 }
1873
1874
1875 if (sourceOnlyAttrs.remove(lowerName) == null)
1876 {
1877 // It wasn't in the set of source attributes, so it must be a
1878 // target-only attribute.
1879 targetOnlyAttrs.put(lowerName,e.getValue());
1880 }
1881 }
1882
1883 for (final String lowerName : sourceOnlyAttrs.keySet())
1884 {
1885 commonAttrs.remove(lowerName);
1886 }
1887
1888 RDN sourceRDN = null;
1889 RDN targetRDN = null;
1890 if (ignoreRDN)
1891 {
1892 try
1893 {
1894 sourceRDN = sourceEntry.getRDN();
1895 }
1896 catch (Exception e)
1897 {
1898 debugException(e);
1899 }
1900
1901 try
1902 {
1903 targetRDN = targetEntry.getRDN();
1904 }
1905 catch (Exception e)
1906 {
1907 debugException(e);
1908 }
1909 }
1910
1911 final ArrayList<Modification> mods = new ArrayList<Modification>(10);
1912
1913 for (final Attribute a : sourceOnlyAttrs.values())
1914 {
1915 if (reversible)
1916 {
1917 ASN1OctetString[] values = a.getRawValues();
1918 if ((sourceRDN != null) && (sourceRDN.hasAttribute(a.getName())))
1919 {
1920 final ArrayList<ASN1OctetString> newValues =
1921 new ArrayList<ASN1OctetString>(values.length);
1922 for (final ASN1OctetString value : values)
1923 {
1924 if (! sourceRDN.hasAttributeValue(a.getName(), value.getValue()))
1925 {
1926 newValues.add(value);
1927 }
1928 }
1929
1930 if (newValues.isEmpty())
1931 {
1932 continue;
1933 }
1934 else
1935 {
1936 values = new ASN1OctetString[newValues.size()];
1937 newValues.toArray(values);
1938 }
1939 }
1940
1941 mods.add(new Modification(ModificationType.DELETE, a.getName(),
1942 values));
1943 }
1944 else
1945 {
1946 mods.add(new Modification(ModificationType.REPLACE, a.getName()));
1947 }
1948 }
1949
1950 for (final Attribute a : targetOnlyAttrs.values())
1951 {
1952 ASN1OctetString[] values = a.getRawValues();
1953 if ((targetRDN != null) && (targetRDN.hasAttribute(a.getName())))
1954 {
1955 final ArrayList<ASN1OctetString> newValues =
1956 new ArrayList<ASN1OctetString>(values.length);
1957 for (final ASN1OctetString value : values)
1958 {
1959 if (! targetRDN.hasAttributeValue(a.getName(), value.getValue()))
1960 {
1961 newValues.add(value);
1962 }
1963 }
1964
1965 if (newValues.isEmpty())
1966 {
1967 continue;
1968 }
1969 else
1970 {
1971 values = new ASN1OctetString[newValues.size()];
1972 newValues.toArray(values);
1973 }
1974 }
1975
1976 if (reversible)
1977 {
1978 mods.add(new Modification(ModificationType.ADD, a.getName(), values));
1979 }
1980 else
1981 {
1982 mods.add(new Modification(ModificationType.REPLACE, a.getName(),
1983 values));
1984 }
1985 }
1986
1987 for (final Attribute sourceAttr : commonAttrs.values())
1988 {
1989 final Attribute targetAttr =
1990 targetEntry.getAttribute(sourceAttr.getName());
1991 if (sourceAttr.equals(targetAttr))
1992 {
1993 continue;
1994 }
1995
1996 if (reversible ||
1997 ((targetRDN != null) && targetRDN.hasAttribute(targetAttr.getName())))
1998 {
1999 final ASN1OctetString[] sourceValueArray = sourceAttr.getRawValues();
2000 final LinkedHashMap<ASN1OctetString,ASN1OctetString> sourceValues =
2001 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
2002 sourceValueArray.length);
2003 for (final ASN1OctetString s : sourceValueArray)
2004 {
2005 try
2006 {
2007 sourceValues.put(sourceAttr.getMatchingRule().normalize(s), s);
2008 }
2009 catch (final Exception e)
2010 {
2011 debugException(e);
2012 sourceValues.put(s, s);
2013 }
2014 }
2015
2016 final ASN1OctetString[] targetValueArray = targetAttr.getRawValues();
2017 final LinkedHashMap<ASN1OctetString,ASN1OctetString> targetValues =
2018 new LinkedHashMap<ASN1OctetString,ASN1OctetString>(
2019 targetValueArray.length);
2020 for (final ASN1OctetString s : targetValueArray)
2021 {
2022 try
2023 {
2024 targetValues.put(sourceAttr.getMatchingRule().normalize(s), s);
2025 }
2026 catch (final Exception e)
2027 {
2028 debugException(e);
2029 targetValues.put(s, s);
2030 }
2031 }
2032
2033 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
2034 sourceIterator = sourceValues.entrySet().iterator();
2035 while (sourceIterator.hasNext())
2036 {
2037 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
2038 sourceIterator.next();
2039 if (targetValues.remove(e.getKey()) != null)
2040 {
2041 sourceIterator.remove();
2042 }
2043 else if ((sourceRDN != null) &&
2044 sourceRDN.hasAttributeValue(sourceAttr.getName(),
2045 e.getValue().getValue()))
2046 {
2047 sourceIterator.remove();
2048 }
2049 }
2050
2051 final Iterator<Map.Entry<ASN1OctetString,ASN1OctetString>>
2052 targetIterator = targetValues.entrySet().iterator();
2053 while (targetIterator.hasNext())
2054 {
2055 final Map.Entry<ASN1OctetString,ASN1OctetString> e =
2056 targetIterator.next();
2057 if ((targetRDN != null) &&
2058 targetRDN.hasAttributeValue(targetAttr.getName(),
2059 e.getValue().getValue()))
2060 {
2061 targetIterator.remove();
2062 }
2063 }
2064
2065 final ArrayList<ASN1OctetString> addValues =
2066 new ArrayList<ASN1OctetString>(targetValues.values());
2067 final ArrayList<ASN1OctetString> delValues =
2068 new ArrayList<ASN1OctetString>(sourceValues.values());
2069
2070 if (! addValues.isEmpty())
2071 {
2072 final ASN1OctetString[] addArray =
2073 new ASN1OctetString[addValues.size()];
2074 mods.add(new Modification(ModificationType.ADD, targetAttr.getName(),
2075 addValues.toArray(addArray)));
2076 }
2077
2078 if (! delValues.isEmpty())
2079 {
2080 final ASN1OctetString[] delArray =
2081 new ASN1OctetString[delValues.size()];
2082 mods.add(new Modification(ModificationType.DELETE,
2083 sourceAttr.getName(), delValues.toArray(delArray)));
2084 }
2085 }
2086 else
2087 {
2088 mods.add(new Modification(ModificationType.REPLACE,
2089 targetAttr.getName(), targetAttr.getRawValues()));
2090 }
2091 }
2092
2093 return mods;
2094 }
2095
2096
2097
2098 /**
2099 * Merges the contents of all provided entries so that the resulting entry
2100 * will contain all attribute values present in at least one of the entries.
2101 *
2102 * @param entries The set of entries to be merged. At least one entry must
2103 * be provided.
2104 *
2105 * @return An entry containing all attribute values present in at least one
2106 * of the entries.
2107 */
2108 public static Entry mergeEntries(final Entry... entries)
2109 {
2110 ensureNotNull(entries);
2111 ensureTrue(entries.length > 0);
2112
2113 final Entry newEntry = entries[0].duplicate();
2114
2115 for (int i=1; i < entries.length; i++)
2116 {
2117 for (final Attribute a : entries[i].attributes.values())
2118 {
2119 newEntry.addAttribute(a);
2120 }
2121 }
2122
2123 return newEntry;
2124 }
2125
2126
2127
2128 /**
2129 * Intersects the contents of all provided entries so that the resulting
2130 * entry will contain only attribute values present in all of the provided
2131 * entries.
2132 *
2133 * @param entries The set of entries to be intersected. At least one entry
2134 * must be provided.
2135 *
2136 * @return An entry containing only attribute values contained in all of the
2137 * provided entries.
2138 */
2139 public static Entry intersectEntries(final Entry... entries)
2140 {
2141 ensureNotNull(entries);
2142 ensureTrue(entries.length > 0);
2143
2144 final Entry newEntry = entries[0].duplicate();
2145
2146 for (final Attribute a : entries[0].attributes.values())
2147 {
2148 final String name = a.getName();
2149 for (final byte[] v : a.getValueByteArrays())
2150 {
2151 for (int i=1; i < entries.length; i++)
2152 {
2153 if (! entries[i].hasAttributeValue(name, v))
2154 {
2155 newEntry.removeAttributeValue(name, v);
2156 break;
2157 }
2158 }
2159 }
2160 }
2161
2162 return newEntry;
2163 }
2164
2165
2166
2167 /**
2168 * Creates a duplicate of the provided entry with the given set of
2169 * modifications applied to it.
2170 *
2171 * @param entry The entry to be modified. It must not be
2172 * {@code null}.
2173 * @param lenient Indicates whether to exhibit a lenient behavior for
2174 * the modifications, which will cause it to ignore
2175 * problems like trying to add values that already
2176 * exist or to remove nonexistent attributes or values.
2177 * @param modifications The set of modifications to apply to the entry. It
2178 * must not be {@code null} or empty.
2179 *
2180 * @return An updated version of the entry with the requested modifications
2181 * applied.
2182 *
2183 * @throws LDAPException If a problem occurs while attempting to apply the
2184 * modifications.
2185 */
2186 public static Entry applyModifications(final Entry entry,
2187 final boolean lenient,
2188 final Modification... modifications)
2189 throws LDAPException
2190 {
2191 ensureNotNull(entry, modifications);
2192 ensureFalse(modifications.length == 0);
2193
2194 return applyModifications(entry, lenient, Arrays.asList(modifications));
2195 }
2196
2197
2198
2199 /**
2200 * Creates a duplicate of the provided entry with the given set of
2201 * modifications applied to it.
2202 *
2203 * @param entry The entry to be modified. It must not be
2204 * {@code null}.
2205 * @param lenient Indicates whether to exhibit a lenient behavior for
2206 * the modifications, which will cause it to ignore
2207 * problems like trying to add values that already
2208 * exist or to remove nonexistent attributes or values.
2209 * @param modifications The set of modifications to apply to the entry. It
2210 * must not be {@code null} or empty.
2211 *
2212 * @return An updated version of the entry with the requested modifications
2213 * applied.
2214 *
2215 * @throws LDAPException If a problem occurs while attempting to apply the
2216 * modifications.
2217 */
2218 public static Entry applyModifications(final Entry entry,
2219 final boolean lenient,
2220 final List<Modification> modifications)
2221 throws LDAPException
2222 {
2223 ensureNotNull(entry, modifications);
2224 ensureFalse(modifications.isEmpty());
2225
2226 final Entry e = entry.duplicate();
2227 final ArrayList<String> errors =
2228 new ArrayList<String>(modifications.size());
2229 ResultCode resultCode = null;
2230
2231 // Get the RDN for the entry to ensure that RDN modifications are not
2232 // allowed.
2233 RDN rdn = null;
2234 try
2235 {
2236 rdn = entry.getRDN();
2237 }
2238 catch (final LDAPException le)
2239 {
2240 debugException(le);
2241 }
2242
2243 for (final Modification m : modifications)
2244 {
2245 final String name = m.getAttributeName();
2246 final byte[][] values = m.getValueByteArrays();
2247 switch (m.getModificationType().intValue())
2248 {
2249 case ModificationType.ADD_INT_VALUE:
2250 if (lenient)
2251 {
2252 e.addAttribute(m.getAttribute());
2253 }
2254 else
2255 {
2256 if (values.length == 0)
2257 {
2258 errors.add(ERR_ENTRY_APPLY_MODS_ADD_NO_VALUES.get(name));
2259 }
2260
2261 for (int i=0; i < values.length; i++)
2262 {
2263 if (! e.addAttribute(name, values[i]))
2264 {
2265 if (resultCode == null)
2266 {
2267 resultCode = ResultCode.ATTRIBUTE_OR_VALUE_EXISTS;
2268 }
2269 errors.add(ERR_ENTRY_APPLY_MODS_ADD_EXISTING.get(
2270 m.getValues()[i], name));
2271 }
2272 }
2273 }
2274 break;
2275
2276 case ModificationType.DELETE_INT_VALUE:
2277 if (values.length == 0)
2278 {
2279 final boolean removed = e.removeAttribute(name);
2280 if (! (lenient || removed))
2281 {
2282 if (resultCode == null)
2283 {
2284 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2285 }
2286 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_ATTR.get(
2287 name));
2288 }
2289 }
2290 else
2291 {
2292 for (int i=0; i < values.length; i++)
2293 {
2294 final boolean removed = e.removeAttributeValue(name, values[i]);
2295 if (! (lenient || removed))
2296 {
2297 if (resultCode == null)
2298 {
2299 resultCode = ResultCode.NO_SUCH_ATTRIBUTE;
2300 }
2301 errors.add(ERR_ENTRY_APPLY_MODS_DELETE_NONEXISTENT_VALUE.get(
2302 m.getValues()[i], name));
2303 }
2304 }
2305 }
2306 break;
2307
2308 case ModificationType.REPLACE_INT_VALUE:
2309 if (values.length == 0)
2310 {
2311 e.removeAttribute(name);
2312 }
2313 else
2314 {
2315 e.setAttribute(m.getAttribute());
2316 }
2317 break;
2318
2319 case ModificationType.INCREMENT_INT_VALUE:
2320 final Attribute a = e.getAttribute(name);
2321 if ((a == null) || (! a.hasValue()))
2322 {
2323 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_SUCH_ATTR.get(name));
2324 continue;
2325 }
2326
2327 if (a.size() > 1)
2328 {
2329 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NOT_SINGLE_VALUED.get(
2330 name));
2331 continue;
2332 }
2333
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 final BigInteger currentValue;
2351 try
2352 {
2353 currentValue = new BigInteger(a.getValue());
2354 }
2355 catch (NumberFormatException nfe)
2356 {
2357 debugException(nfe);
2358 errors.add(
2359 ERR_ENTRY_APPLY_MODS_INCREMENT_ENTRY_VALUE_NOT_INTEGER.get(
2360 name, a.getValue()));
2361 continue;
2362 }
2363
2364 if (values.length == 0)
2365 {
2366 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_NO_MOD_VALUES.get(name));
2367 continue;
2368 }
2369 else if (values.length > 1)
2370 {
2371 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MULTIPLE_MOD_VALUES.get(
2372 name));
2373 continue;
2374 }
2375
2376 final BigInteger incrementValue;
2377 final String incrementValueStr = m.getValues()[0];
2378 try
2379 {
2380 incrementValue = new BigInteger(incrementValueStr);
2381 }
2382 catch (NumberFormatException nfe)
2383 {
2384 debugException(nfe);
2385 errors.add(ERR_ENTRY_APPLY_MODS_INCREMENT_MOD_VALUE_NOT_INTEGER.get(
2386 name, incrementValueStr));
2387 continue;
2388 }
2389
2390 final BigInteger newValue = currentValue.add(incrementValue);
2391 e.setAttribute(name, newValue.toString());
2392 break;
2393
2394 default:
2395 errors.add(ERR_ENTRY_APPLY_MODS_UNKNOWN_TYPE.get(
2396 String.valueOf(m.getModificationType())));
2397 break;
2398 }
2399 }
2400
2401
2402 // Make sure that the entry still has all of the RDN attribute values.
2403 if (rdn != null)
2404 {
2405 final String[] rdnAttrs = rdn.getAttributeNames();
2406 final byte[][] rdnValues = rdn.getByteArrayAttributeValues();
2407 for (int i=0; i < rdnAttrs.length; i++)
2408 {
2409 if (! e.hasAttributeValue(rdnAttrs[i], rdnValues[i]))
2410 {
2411 errors.add(ERR_ENTRY_APPLY_MODS_TARGETS_RDN.get(entry.getDN()));
2412 if (resultCode == null)
2413 {
2414 resultCode = ResultCode.NOT_ALLOWED_ON_RDN;
2415 }
2416 break;
2417 }
2418 }
2419 }
2420
2421
2422 if (errors.isEmpty())
2423 {
2424 return e;
2425 }
2426
2427 if (resultCode == null)
2428 {
2429 resultCode = ResultCode.CONSTRAINT_VIOLATION;
2430 }
2431
2432 throw new LDAPException(resultCode,
2433 ERR_ENTRY_APPLY_MODS_FAILURE.get(e.getDN(),
2434 concatenateStrings(errors)));
2435 }
2436
2437
2438
2439 /**
2440 * Generates a hash code for this entry.
2441 *
2442 * @return The generated hash code for this entry.
2443 */
2444 @Override()
2445 public int hashCode()
2446 {
2447 int hashCode = 0;
2448 try
2449 {
2450 hashCode += getParsedDN().hashCode();
2451 }
2452 catch (LDAPException le)
2453 {
2454 debugException(le);
2455 hashCode += dn.hashCode();
2456 }
2457
2458 for (final Attribute a : attributes.values())
2459 {
2460 hashCode += a.hashCode();
2461 }
2462
2463 return hashCode;
2464 }
2465
2466
2467
2468 /**
2469 * Indicates whether the provided object is equal to this entry. The provided
2470 * object will only be considered equal to this entry if it is an entry with
2471 * the same DN and set of attributes.
2472 *
2473 * @param o The object for which to make the determination.
2474 *
2475 * @return {@code true} if the provided object is considered equal to this
2476 * entry, or {@code false} if not.
2477 */
2478 @Override()
2479 public boolean equals(final Object o)
2480 {
2481 if (o == null)
2482 {
2483 return false;
2484 }
2485
2486 if (o == this)
2487 {
2488 return true;
2489 }
2490
2491 if (! (o instanceof Entry))
2492 {
2493 return false;
2494 }
2495
2496 final Entry e = (Entry) o;
2497
2498 try
2499 {
2500 final DN thisDN = getParsedDN();
2501 final DN thatDN = e.getParsedDN();
2502 if (! thisDN.equals(thatDN))
2503 {
2504 return false;
2505 }
2506 }
2507 catch (LDAPException le)
2508 {
2509 debugException(le);
2510 if (! dn.equals(e.dn))
2511 {
2512 return false;
2513 }
2514 }
2515
2516 if (attributes.size() != e.attributes.size())
2517 {
2518 return false;
2519 }
2520
2521 for (final Attribute a : attributes.values())
2522 {
2523 if (! e.hasAttribute(a))
2524 {
2525 return false;
2526 }
2527 }
2528
2529 return true;
2530 }
2531
2532
2533
2534 /**
2535 * Creates a new entry that is a duplicate of this entry.
2536 *
2537 * @return A new entry that is a duplicate of this entry.
2538 */
2539 public Entry duplicate()
2540 {
2541 return new Entry(dn, schema, attributes.values());
2542 }
2543
2544
2545
2546 /**
2547 * Retrieves an LDIF representation of this entry, with each attribute value
2548 * on a separate line. Long lines will not be wrapped.
2549 *
2550 * @return An LDIF representation of this entry.
2551 */
2552 public final String[] toLDIF()
2553 {
2554 return toLDIF(0);
2555 }
2556
2557
2558
2559 /**
2560 * Retrieves an LDIF representation of this entry, with each attribute value
2561 * on a separate line. Long lines will be wrapped at the specified column.
2562 *
2563 * @param wrapColumn The column at which long lines should be wrapped. A
2564 * value less than or equal to two indicates that no
2565 * wrapping should be performed.
2566 *
2567 * @return An LDIF representation of this entry.
2568 */
2569 public final String[] toLDIF(final int wrapColumn)
2570 {
2571 List<String> ldifLines = new ArrayList<String>(2*attributes.size());
2572 ldifLines.add(LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn)));
2573
2574 for (final Attribute a : attributes.values())
2575 {
2576 final String name = a.getName();
2577 for (final ASN1OctetString value : a.getRawValues())
2578 {
2579 ldifLines.add(LDIFWriter.encodeNameAndValue(name, value));
2580 }
2581 }
2582
2583 if (wrapColumn > 2)
2584 {
2585 ldifLines = LDIFWriter.wrapLines(wrapColumn, ldifLines);
2586 }
2587
2588 final String[] lineArray = new String[ldifLines.size()];
2589 ldifLines.toArray(lineArray);
2590 return lineArray;
2591 }
2592
2593
2594
2595 /**
2596 * Appends an LDIF representation of this entry to the provided buffer. Long
2597 * lines will not be wrapped.
2598 *
2599 * @param buffer The buffer to which the LDIF representation of this entry
2600 * should be written.
2601 */
2602 public final void toLDIF(final ByteStringBuffer buffer)
2603 {
2604 toLDIF(buffer, 0);
2605 }
2606
2607
2608
2609 /**
2610 * Appends an LDIF representation of this entry to the provided buffer.
2611 *
2612 * @param buffer The buffer to which the LDIF representation of this
2613 * entry should be written.
2614 * @param wrapColumn The column at which long lines should be wrapped. A
2615 * value less than or equal to two indicates that no
2616 * wrapping should be performed.
2617 */
2618 public final void toLDIF(final ByteStringBuffer buffer, final int wrapColumn)
2619 {
2620 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2621 wrapColumn);
2622 buffer.append(EOL_BYTES);
2623
2624 for (final Attribute a : attributes.values())
2625 {
2626 final String name = a.getName();
2627 for (final ASN1OctetString value : a.getRawValues())
2628 {
2629 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2630 buffer.append(EOL_BYTES);
2631 }
2632 }
2633 }
2634
2635
2636
2637 /**
2638 * Retrieves an LDIF-formatted string representation of this entry. No
2639 * wrapping will be performed, and no extra blank lines will be added.
2640 *
2641 * @return An LDIF-formatted string representation of this entry.
2642 */
2643 public final String toLDIFString()
2644 {
2645 final StringBuilder buffer = new StringBuilder();
2646 toLDIFString(buffer, 0);
2647 return buffer.toString();
2648 }
2649
2650
2651
2652 /**
2653 * Retrieves an LDIF-formatted string representation of this entry. No
2654 * extra blank lines will be added.
2655 *
2656 * @param wrapColumn The column at which long lines should be wrapped. A
2657 * value less than or equal to two indicates that no
2658 * wrapping should be performed.
2659 *
2660 * @return An LDIF-formatted string representation of this entry.
2661 */
2662 public final String toLDIFString(final int wrapColumn)
2663 {
2664 final StringBuilder buffer = new StringBuilder();
2665 toLDIFString(buffer, wrapColumn);
2666 return buffer.toString();
2667 }
2668
2669
2670
2671 /**
2672 * Appends an LDIF-formatted string representation of this entry to the
2673 * provided buffer. No wrapping will be performed, and no extra blank lines
2674 * will be added.
2675 *
2676 * @param buffer The buffer to which to append the LDIF representation of
2677 * this entry.
2678 */
2679 public final void toLDIFString(final StringBuilder buffer)
2680 {
2681 toLDIFString(buffer, 0);
2682 }
2683
2684
2685
2686 /**
2687 * Appends an LDIF-formatted string representation of this entry to the
2688 * provided buffer. No extra blank lines will be added.
2689 *
2690 * @param buffer The buffer to which to append the LDIF representation
2691 * of this entry.
2692 * @param wrapColumn The column at which long lines should be wrapped. A
2693 * value less than or equal to two indicates that no
2694 * wrapping should be performed.
2695 */
2696 public final void toLDIFString(final StringBuilder buffer,
2697 final int wrapColumn)
2698 {
2699 LDIFWriter.encodeNameAndValue("dn", new ASN1OctetString(dn), buffer,
2700 wrapColumn);
2701 buffer.append(EOL);
2702
2703 for (final Attribute a : attributes.values())
2704 {
2705 final String name = a.getName();
2706 for (final ASN1OctetString value : a.getRawValues())
2707 {
2708 LDIFWriter.encodeNameAndValue(name, value, buffer, wrapColumn);
2709 buffer.append(EOL);
2710 }
2711 }
2712 }
2713
2714
2715
2716 /**
2717 * Retrieves a string representation of this entry.
2718 *
2719 * @return A string representation of this entry.
2720 */
2721 @Override()
2722 public final String toString()
2723 {
2724 final StringBuilder buffer = new StringBuilder();
2725 toString(buffer);
2726 return buffer.toString();
2727 }
2728
2729
2730
2731 /**
2732 * Appends a string representation of this entry to the provided buffer.
2733 *
2734 * @param buffer The buffer to which to append the string representation of
2735 * this entry.
2736 */
2737 public void toString(final StringBuilder buffer)
2738 {
2739 buffer.append("Entry(dn='");
2740 buffer.append(dn);
2741 buffer.append("', attributes={");
2742
2743 final Iterator<Attribute> iterator = attributes.values().iterator();
2744
2745 while (iterator.hasNext())
2746 {
2747 iterator.next().toString(buffer);
2748 if (iterator.hasNext())
2749 {
2750 buffer.append(", ");
2751 }
2752 }
2753
2754 buffer.append("})");
2755 }
2756 }