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