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