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 */ 021package com.unboundid.ldap.sdk; 022 023 024 025import java.math.BigInteger; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.Collection; 029import java.util.Collections; 030import java.util.Date; 031import java.util.HashSet; 032import java.util.Iterator; 033import java.util.LinkedHashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037 038import com.unboundid.asn1.ASN1OctetString; 039import com.unboundid.ldap.matchingrules.MatchingRule; 040import com.unboundid.ldap.sdk.schema.AttributeTypeDefinition; 041import com.unboundid.ldap.sdk.schema.Schema; 042import com.unboundid.ldif.LDIFException; 043import com.unboundid.ldif.LDIFReader; 044import com.unboundid.ldif.LDIFRecord; 045import com.unboundid.ldif.LDIFWriter; 046import com.unboundid.util.ByteStringBuffer; 047import com.unboundid.util.Mutable; 048import com.unboundid.util.NotExtensible; 049import com.unboundid.util.ThreadSafety; 050import com.unboundid.util.ThreadSafetyLevel; 051 052import static com.unboundid.ldap.sdk.LDAPMessages.*; 053import static com.unboundid.util.Debug.*; 054import static com.unboundid.util.StaticUtils.*; 055import 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) 090public 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}