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