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