001 /* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005 /* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021 package com.unboundid.ldap.sdk; 022 023 024 025 import java.io.Serializable; 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.LinkedHashSet; 034 import java.util.Set; 035 036 import com.unboundid.asn1.ASN1Buffer; 037 import com.unboundid.asn1.ASN1BufferSequence; 038 import com.unboundid.asn1.ASN1BufferSet; 039 import com.unboundid.asn1.ASN1Element; 040 import com.unboundid.asn1.ASN1Exception; 041 import com.unboundid.asn1.ASN1OctetString; 042 import com.unboundid.asn1.ASN1Sequence; 043 import com.unboundid.asn1.ASN1Set; 044 import com.unboundid.asn1.ASN1StreamReader; 045 import com.unboundid.asn1.ASN1StreamReaderSet; 046 import com.unboundid.ldap.matchingrules.CaseIgnoreStringMatchingRule; 047 import com.unboundid.ldap.matchingrules.MatchingRule; 048 import com.unboundid.ldap.sdk.schema.Schema; 049 import com.unboundid.util.Base64; 050 051 import static com.unboundid.ldap.sdk.LDAPMessages.*; 052 import static com.unboundid.util.Debug.*; 053 import static com.unboundid.util.StaticUtils.*; 054 import static com.unboundid.util.Validator.*; 055 056 057 058 /** 059 * This class provides a data structure for holding information about an LDAP 060 * attribute, which includes an attribute name (which may include a set of 061 * attribute options) and zero or more values. Attribute objects are immutable 062 * and cannot be altered. However, if an attribute is included in an 063 * {@code Entry} object, then it is possible to add and remove attribute values 064 * from the entry (which will actually create new Attribute object instances), 065 * although this is not allowed for instances of {@code ReadOnlyEntry} and its 066 * subclasses. 067 * <BR><BR> 068 * This class uses the term "attribute name" as an equivalent of what the LDAP 069 * specification refers to as an "attribute description". An attribute 070 * description consists of an attribute type name or object identifier (which 071 * this class refers to as the "base name") followed by zero or more attribute 072 * options, each of which should be prefixed by a semicolon. Attribute options 073 * may be used to provide additional metadata for the attribute and/or its 074 * values, or to indicate special handling for the values. For example, 075 * <A HREF="http://www.ietf.org/rfc/rfc3866.txt">RFC 3866</A> describes the use 076 * of attribute options to indicate that a value may be associated with a 077 * particular language (e.g., "cn;lang-en-US" indicates that the values of that 078 * cn attribute should be treated as U.S. English values), and 079 * <A HREF="http://www.ietf.org/rfc/rfc4522.txt">RFC 4522</A> describes a binary 080 * encoding option that indicates that the server should only attempt to 081 * interact with the values as binary data (e.g., "userCertificate;binary") and 082 * should not treat them as strings. An attribute name (which is technically 083 * referred to as an "attribute description" in the protocol specification) may 084 * have zero, one, or multiple attribute options. If there are any attribute 085 * options, then a semicolon is used to separate the first option from the base 086 * attribute name, and to separate each subsequent attribute option from the 087 * previous option. 088 * <BR><BR> 089 * Attribute values can be treated as either strings or byte arrays. In LDAP, 090 * they are always transferred using a binary encoding, but applications 091 * frequently treat them as strings and it is often more convenient to do so. 092 * However, for some kinds of data (e.g., certificates, images, audio clips, and 093 * other "blobs") it may be desirable to only treat them as binary data and only 094 * interact with the values as byte arrays. If you do intend to interact with 095 * string values as byte arrays, then it is important to ensure that you use a 096 * UTF-8 representation for those values unless you are confident that the 097 * directory server will not attempt to treat the value as a string. 098 */ 099 public final class Attribute 100 implements Serializable 101 { 102 /** 103 * The array to use as the set of values when there are no values. 104 */ 105 private static final ASN1OctetString[] NO_VALUES = new ASN1OctetString[0]; 106 107 108 109 /** 110 * The array to use as the set of byte array values when there are no values. 111 */ 112 private static final byte[][] NO_BYTE_VALUES = new byte[0][]; 113 114 115 116 /** 117 * The serial version UID for this serializable class. 118 */ 119 private static final long serialVersionUID = 5867076498293567612L; 120 121 122 123 // The set of values for this attribute. 124 private final ASN1OctetString[] values; 125 126 // The hash code for this attribute. 127 private int hashCode = -1; 128 129 // The matching rule that should be used for equality determinations. 130 private final MatchingRule matchingRule; 131 132 // The attribute description for this attribute. 133 private final String name; 134 135 136 137 /** 138 * Creates a new LDAP attribute with the specified name and no values. 139 * 140 * @param name The name for this attribute. It must not be {@code null}. 141 */ 142 public Attribute(final String name) 143 { 144 ensureNotNull(name); 145 146 this.name = name; 147 148 values = NO_VALUES; 149 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 150 } 151 152 153 154 /** 155 * Creates a new LDAP attribute with the specified name and value. 156 * 157 * @param name The name for this attribute. It must not be {@code null}. 158 * @param value The value for this attribute. It must not be {@code null}. 159 */ 160 public Attribute(final String name, final String value) 161 { 162 ensureNotNull(name, value); 163 164 this.name = name; 165 166 values = new ASN1OctetString[] { new ASN1OctetString(value) }; 167 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 168 } 169 170 171 172 /** 173 * Creates a new LDAP attribute with the specified name and value. 174 * 175 * @param name The name for this attribute. It must not be {@code null}. 176 * @param value The value for this attribute. It must not be {@code null}. 177 */ 178 public Attribute(final String name, final byte[] value) 179 { 180 ensureNotNull(name, value); 181 182 this.name = name; 183 values = new ASN1OctetString[] { new ASN1OctetString(value) }; 184 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 185 } 186 187 188 189 /** 190 * Creates a new LDAP attribute with the specified name and set of values. 191 * 192 * @param name The name for this attribute. It must not be {@code null}. 193 * @param values The set of values for this attribute. It must not be 194 * {@code null}. 195 */ 196 public Attribute(final String name, final String... values) 197 { 198 ensureNotNull(name, values); 199 200 this.name = name; 201 202 this.values = new ASN1OctetString[values.length]; 203 for (int i=0; i < values.length; i++) 204 { 205 this.values[i] = new ASN1OctetString(values[i]); 206 } 207 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 208 } 209 210 211 212 /** 213 * Creates a new LDAP attribute with the specified name and set of values. 214 * 215 * @param name The name for this attribute. It must not be {@code null}. 216 * @param values The set of values for this attribute. It must not be 217 * {@code null}. 218 */ 219 public Attribute(final String name, final byte[]... values) 220 { 221 ensureNotNull(name, values); 222 223 this.name = name; 224 225 this.values = new ASN1OctetString[values.length]; 226 for (int i=0; i < values.length; i++) 227 { 228 this.values[i] = new ASN1OctetString(values[i]); 229 } 230 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 231 } 232 233 234 235 /** 236 * Creates a new LDAP attribute with the specified name and set of values. 237 * 238 * @param name The name for this attribute. It must not be {@code null}. 239 * @param values The set of raw values for this attribute. It must not be 240 * {@code null}. 241 */ 242 public Attribute(final String name, final ASN1OctetString... values) 243 { 244 ensureNotNull(name, values); 245 246 this.name = name; 247 this.values = values; 248 249 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 250 } 251 252 253 254 /** 255 * Creates a new LDAP attribute with the specified name and set of values. 256 * 257 * @param name The name for this attribute. It must not be {@code null}. 258 * @param values The set of values for this attribute. It must not be 259 * {@code null}. 260 */ 261 public Attribute(final String name, final Collection<String> values) 262 { 263 ensureNotNull(name, values); 264 265 this.name = name; 266 267 this.values = new ASN1OctetString[values.size()]; 268 269 int i=0; 270 for (final String s : values) 271 { 272 this.values[i++] = new ASN1OctetString(s); 273 } 274 matchingRule = CaseIgnoreStringMatchingRule.getInstance(); 275 } 276 277 278 279 /** 280 * Creates a new LDAP attribute with the specified name and no values. 281 * 282 * @param name The name for this attribute. It must not be 283 * {@code null}. 284 * @param matchingRule The matching rule to use when comparing values. It 285 * must not be {@code null}. 286 */ 287 public Attribute(final String name, final MatchingRule matchingRule) 288 { 289 ensureNotNull(name, matchingRule); 290 291 this.name = name; 292 this.matchingRule = matchingRule; 293 294 values = NO_VALUES; 295 } 296 297 298 299 /** 300 * Creates a new LDAP attribute with the specified name and value. 301 * 302 * @param name The name for this attribute. It must not be 303 * {@code null}. 304 * @param matchingRule The matching rule to use when comparing values. It 305 * must not be {@code null}. 306 * @param value The value for this attribute. It must not be 307 * {@code null}. 308 */ 309 public Attribute(final String name, final MatchingRule matchingRule, 310 final String value) 311 { 312 ensureNotNull(name, matchingRule, value); 313 314 this.name = name; 315 this.matchingRule = matchingRule; 316 317 values = new ASN1OctetString[] { new ASN1OctetString(value) }; 318 } 319 320 321 322 /** 323 * Creates a new LDAP attribute with the specified name and value. 324 * 325 * @param name The name for this attribute. It must not be 326 * {@code null}. 327 * @param matchingRule The matching rule to use when comparing values. It 328 * must not be {@code null}. 329 * @param value The value for this attribute. It must not be 330 * {@code null}. 331 */ 332 public Attribute(final String name, final MatchingRule matchingRule, 333 final byte[] value) 334 { 335 ensureNotNull(name, matchingRule, value); 336 337 this.name = name; 338 this.matchingRule = matchingRule; 339 340 values = new ASN1OctetString[] { new ASN1OctetString(value) }; 341 } 342 343 344 345 /** 346 * Creates a new LDAP attribute with the specified name and set of values. 347 * 348 * @param name The name for this attribute. It must not be 349 * {@code null}. 350 * @param matchingRule The matching rule to use when comparing values. It 351 * must not be {@code null}. 352 * @param values The set of values for this attribute. It must not be 353 * {@code null}. 354 */ 355 public Attribute(final String name, final MatchingRule matchingRule, 356 final String... values) 357 { 358 ensureNotNull(name, matchingRule, values); 359 360 this.name = name; 361 this.matchingRule = matchingRule; 362 363 this.values = new ASN1OctetString[values.length]; 364 for (int i=0; i < values.length; i++) 365 { 366 this.values[i] = new ASN1OctetString(values[i]); 367 } 368 } 369 370 371 372 /** 373 * Creates a new LDAP attribute with the specified name and set of values. 374 * 375 * @param name The name for this attribute. It must not be 376 * {@code null}. 377 * @param matchingRule The matching rule to use when comparing values. It 378 * must not be {@code null}. 379 * @param values The set of values for this attribute. It must not be 380 * {@code null}. 381 */ 382 public Attribute(final String name, final MatchingRule matchingRule, 383 final byte[]... values) 384 { 385 ensureNotNull(name, matchingRule, values); 386 387 this.name = name; 388 this.matchingRule = matchingRule; 389 390 this.values = new ASN1OctetString[values.length]; 391 for (int i=0; i < values.length; i++) 392 { 393 this.values[i] = new ASN1OctetString(values[i]); 394 } 395 } 396 397 398 399 /** 400 * Creates a new LDAP attribute with the specified name and set of values. 401 * 402 * @param name The name for this attribute. It must not be 403 * {@code null}. 404 * @param matchingRule The matching rule to use when comparing values. It 405 * must not be {@code null}. 406 * @param values The set of values for this attribute. It must not be 407 * {@code null}. 408 */ 409 public Attribute(final String name, final MatchingRule matchingRule, 410 final Collection<String> values) 411 { 412 ensureNotNull(name, matchingRule, values); 413 414 this.name = name; 415 this.matchingRule = matchingRule; 416 417 this.values = new ASN1OctetString[values.size()]; 418 419 int i=0; 420 for (final String s : values) 421 { 422 this.values[i++] = new ASN1OctetString(s); 423 } 424 } 425 426 427 428 /** 429 * Creates a new LDAP attribute with the specified name and set of values. 430 * 431 * @param name The name for this attribute. 432 * @param matchingRule The matching rule for this attribute. 433 * @param values The set of values for this attribute. 434 */ 435 public Attribute(final String name, final MatchingRule matchingRule, 436 final ASN1OctetString[] values) 437 { 438 this.name = name; 439 this.matchingRule = matchingRule; 440 this.values = values; 441 } 442 443 444 445 /** 446 * Creates a new LDAP attribute with the specified name and set of values. 447 * 448 * @param name The name for this attribute. It must not be {@code null}. 449 * @param schema The schema to use to select the matching rule for this 450 * attribute. It may be {@code null} if the default matching 451 * rule should be used. 452 * @param values The set of values for this attribute. It must not be 453 * {@code null}. 454 */ 455 public Attribute(final String name, final Schema schema, 456 final String... values) 457 { 458 this(name, MatchingRule.selectEqualityMatchingRule(name, schema), values); 459 } 460 461 462 463 /** 464 * Creates a new LDAP attribute with the specified name and set of values. 465 * 466 * @param name The name for this attribute. It must not be {@code null}. 467 * @param schema The schema to use to select the matching rule for this 468 * attribute. It may be {@code null} if the default matching 469 * rule should be used. 470 * @param values The set of values for this attribute. It must not be 471 * {@code null}. 472 */ 473 public Attribute(final String name, final Schema schema, 474 final byte[]... values) 475 { 476 this(name, MatchingRule.selectEqualityMatchingRule(name, schema), values); 477 } 478 479 480 481 /** 482 * Creates a new LDAP attribute with the specified name and set of values. 483 * 484 * @param name The name for this attribute. It must not be {@code null}. 485 * @param schema The schema to use to select the matching rule for this 486 * attribute. It may be {@code null} if the default matching 487 * rule should be used. 488 * @param values The set of values for this attribute. It must not be 489 * {@code null}. 490 */ 491 public Attribute(final String name, final Schema schema, 492 final Collection<String> values) 493 { 494 this(name, MatchingRule.selectEqualityMatchingRule(name, schema), values); 495 } 496 497 498 499 /** 500 * Creates a new LDAP attribute with the specified name and set of values. 501 * 502 * @param name The name for this attribute. It must not be {@code null}. 503 * @param schema The schema to use to select the matching rule for this 504 * attribute. It may be {@code null} if the default matching 505 * rule should be used. 506 * @param values The set of values for this attribute. It must not be 507 * {@code null}. 508 */ 509 public Attribute(final String name, final Schema schema, 510 final ASN1OctetString[] values) 511 { 512 this(name, MatchingRule.selectEqualityMatchingRule(name, schema), values); 513 } 514 515 516 517 /** 518 * Creates a new attribute containing the merged values of the provided 519 * attributes. Any duplicate values will only be present once in the 520 * resulting attribute. The names of the provided attributes must be the 521 * same. 522 * 523 * @param attr1 The first attribute containing the values to merge. It must 524 * not be {@code null}. 525 * @param attr2 The second attribute containing the values to merge. It 526 * must not be {@code null}. 527 * 528 * @return The new attribute containing the values of both of the 529 * provided attributes. 530 */ 531 public static Attribute mergeAttributes(final Attribute attr1, 532 final Attribute attr2) 533 { 534 ensureNotNull(attr1, attr2); 535 536 final String name = attr1.name; 537 ensureTrue(name.equalsIgnoreCase(attr2.name)); 538 539 final MatchingRule matchingRule = attr1.matchingRule; 540 541 ASN1OctetString[] mergedValues = 542 new ASN1OctetString[attr1.values.length + attr2.values.length]; 543 System.arraycopy(attr1.values, 0, mergedValues, 0, attr1.values.length); 544 545 int pos = attr1.values.length; 546 for (final ASN1OctetString s2 : attr2.values) 547 { 548 boolean found = false; 549 for (final ASN1OctetString s1 : attr1.values) 550 { 551 try 552 { 553 if (matchingRule.valuesMatch(s1, s2)) 554 { 555 found = true; 556 break; 557 } 558 } 559 catch (Exception e) 560 { 561 debugException(e); 562 } 563 } 564 565 if (! found) 566 { 567 mergedValues[pos++] = s2; 568 } 569 } 570 571 if (pos != mergedValues.length) 572 { 573 // This indicates that there were duplicate values. 574 final ASN1OctetString[] newMergedValues = new ASN1OctetString[pos]; 575 System.arraycopy(mergedValues, 0, newMergedValues, 0, pos); 576 mergedValues = newMergedValues; 577 } 578 579 return new Attribute(name, matchingRule, mergedValues); 580 } 581 582 583 584 /** 585 * Creates a new attribute containing all of the values of the first attribute 586 * that are not contained in the second attribute. Any values contained in 587 * the second attribute that are not contained in the first will be ignored. 588 * The names of the provided attributes must be the same. 589 * 590 * @param attr1 The attribute from which to remove the values. It must not 591 * be {@code null}. 592 * @param attr2 The attribute containing the values to remove. It must not 593 * be {@code null}. 594 * 595 * @return A new attribute containing all of the values of the first 596 * attribute not contained in the second. It may contain zero values 597 * if all the values of the first attribute were also contained in 598 * the second. 599 */ 600 public static Attribute removeValues(final Attribute attr1, 601 final Attribute attr2) 602 { 603 return removeValues(attr1, attr2, attr1.matchingRule); 604 } 605 606 607 608 /** 609 * Creates a new attribute containing all of the values of the first attribute 610 * that are not contained in the second attribute. Any values contained in 611 * the second attribute that are not contained in the first will be ignored. 612 * The names of the provided attributes must be the same. 613 * 614 * @param attr1 The attribute from which to remove the values. It 615 * must not be {@code null}. 616 * @param attr2 The attribute containing the values to remove. It 617 * must not be {@code null}. 618 * @param matchingRule The matching rule to use to locate matching values. 619 * It may be {@code null} if the matching rule 620 * associated with the first attribute should be used. 621 * 622 * @return A new attribute containing all of the values of the first 623 * attribute not contained in the second. It may contain zero values 624 * if all the values of the first attribute were also contained in 625 * the second. 626 */ 627 public static Attribute removeValues(final Attribute attr1, 628 final Attribute attr2, 629 final MatchingRule matchingRule) 630 { 631 ensureNotNull(attr1, attr2); 632 633 final String name = attr1.name; 634 ensureTrue(name.equalsIgnoreCase(attr2.name)); 635 636 final MatchingRule mr; 637 if (matchingRule == null) 638 { 639 mr = attr1.matchingRule; 640 } 641 else 642 { 643 mr = matchingRule; 644 } 645 646 final ArrayList<ASN1OctetString> newValues = 647 new ArrayList<ASN1OctetString>(Arrays.asList(attr1.values)); 648 649 final Iterator<ASN1OctetString> iterator = newValues.iterator(); 650 while (iterator.hasNext()) 651 { 652 if (attr2.hasValue(iterator.next(), mr)) 653 { 654 iterator.remove(); 655 } 656 } 657 658 final ASN1OctetString[] newValueArray = 659 new ASN1OctetString[newValues.size()]; 660 newValues.toArray(newValueArray); 661 662 return new Attribute(name, mr, newValueArray); 663 } 664 665 666 667 /** 668 * Retrieves the name for this attribute (i.e., the attribute description), 669 * which may include zero or more attribute options. 670 * 671 * @return The name for this attribute. 672 */ 673 public String getName() 674 { 675 return name; 676 } 677 678 679 680 /** 681 * Retrieves the base name for this attribute, which is the name or OID of the 682 * attribute type, without any attribute options. For an attribute without 683 * any options, the value returned by this method will be identical the value 684 * returned by the {@code getName} method. 685 * 686 * @return The base name for this attribute. 687 */ 688 public String getBaseName() 689 { 690 return getBaseName(name); 691 } 692 693 694 695 /** 696 * Retrieves the base name for an attribute with the given name, which will be 697 * the provided name without any attribute options. If the given name does 698 * not include any attribute options, then it will be returned unaltered. If 699 * it does contain one or more attribute options, then the name will be 700 * returned without those options. 701 * 702 * @param name The name to be processed. 703 * 704 * @return The base name determined from the provided attribute name. 705 */ 706 public static String getBaseName(final String name) 707 { 708 final int semicolonPos = name.indexOf(';'); 709 if (semicolonPos > 0) 710 { 711 return name.substring(0, semicolonPos); 712 } 713 else 714 { 715 return name; 716 } 717 } 718 719 720 721 /** 722 * Indicates whether the name of this attribute is valid as per RFC 4512. The 723 * name will be considered valid only if it starts with an ASCII alphabetic 724 * character ('a' through 'z', or 'A' through 'Z'), and contains only ASCII 725 * alphabetic characters, ASCII numeric digits ('0' through '9'), and the 726 * ASCII hyphen character ('-'). It will also be allowed to include zero or 727 * more attribute options, in which the option must be separate from the base 728 * name by a semicolon and has the same naming constraints as the base name. 729 * 730 * @return {@code true} if this attribute has a valid name, or {@code false} 731 * if not. 732 */ 733 public boolean nameIsValid() 734 { 735 return nameIsValid(name, true); 736 } 737 738 739 740 /** 741 * Indicates whether the provided string represents a valid attribute name as 742 * per RFC 4512. It will be considered valid only if it starts with an ASCII 743 * alphabetic character ('a' through 'z', or 'A' through 'Z'), and contains 744 * only ASCII alphabetic characters, ASCII numeric digits ('0' through '9'), 745 * and the ASCII hyphen character ('-'). It will also be allowed to include 746 * zero or more attribute options, in which the option must be separate from 747 * the base name by a semicolon and has the same naming constraints as the 748 * base name. 749 * 750 * @param s The name for which to make the determination. 751 * 752 * @return {@code true} if this attribute has a valid name, or {@code false} 753 * if not. 754 */ 755 public static boolean nameIsValid(final String s) 756 { 757 return nameIsValid(s, true); 758 } 759 760 761 762 /** 763 * Indicates whether the provided string represents a valid attribute name as 764 * per RFC 4512. It will be considered valid only if it starts with an ASCII 765 * alphabetic character ('a' through 'z', or 'A' through 'Z'), and contains 766 * only ASCII alphabetic characters, ASCII numeric digits ('0' through '9'), 767 * and the ASCII hyphen character ('-'). It may optionally be allowed to 768 * include zero or more attribute options, in which the option must be 769 * separate from the base name by a semicolon and has the same naming 770 * constraints as the base name. 771 * 772 * @param s The name for which to make the determination. 773 * @param allowOptions Indicates whether the provided name will be allowed 774 * to contain attribute options. 775 * 776 * @return {@code true} if this attribute has a valid name, or {@code false} 777 * if not. 778 */ 779 public static boolean nameIsValid(final String s, final boolean allowOptions) 780 { 781 final int length; 782 if ((s == null) || ((length = s.length()) == 0)) 783 { 784 return false; 785 } 786 787 final char firstChar = s.charAt(0); 788 if (! (((firstChar >= 'a') && (firstChar <= 'z')) || 789 ((firstChar >= 'A') && (firstChar <= 'Z')))) 790 { 791 return false; 792 } 793 794 boolean lastWasSemiColon = false; 795 for (int i=1; i < length; i++) 796 { 797 final char c = s.charAt(i); 798 if (((c >= 'a') && (c <= 'z')) || 799 ((c >= 'A') && (c <= 'Z'))) 800 { 801 // This will always be acceptable. 802 lastWasSemiColon = false; 803 } 804 else if (((c >= '0') && (c <= '9')) || 805 (c == '-')) 806 { 807 // These will only be acceptable if the last character was not a 808 // semicolon. 809 if (lastWasSemiColon) 810 { 811 return false; 812 } 813 814 lastWasSemiColon = false; 815 } 816 else if (c == ';') 817 { 818 // This will only be acceptable if attribute options are allowed and the 819 // last character was not a semicolon. 820 if (lastWasSemiColon || (! allowOptions)) 821 { 822 return false; 823 } 824 825 lastWasSemiColon = true; 826 } 827 else 828 { 829 return false; 830 } 831 } 832 833 return (! lastWasSemiColon); 834 } 835 836 837 838 /** 839 * Indicates whether this attribute has any attribute options. 840 * 841 * @return {@code true} if this attribute has at least one attribute option, 842 * or {@code false} if not. 843 */ 844 public boolean hasOptions() 845 { 846 return hasOptions(name); 847 } 848 849 850 851 /** 852 * Indicates whether the provided attribute name contains any options. 853 * 854 * @param name The name for which to make the determination. 855 * 856 * @return {@code true} if the provided attribute name has at least one 857 * attribute option, or {@code false} if not. 858 */ 859 public static boolean hasOptions(final String name) 860 { 861 return (name.indexOf(';') > 0); 862 } 863 864 865 866 /** 867 * Indicates whether this attribute has the specified attribute option. 868 * 869 * @param option The attribute option for which to make the determination. 870 * 871 * @return {@code true} if this attribute has the specified attribute option, 872 * or {@code false} if not. 873 */ 874 public boolean hasOption(final String option) 875 { 876 return hasOption(name, option); 877 } 878 879 880 881 /** 882 * Indicates whether the provided attribute name has the specified attribute 883 * option. 884 * 885 * @param name The name to be examined. 886 * @param option The attribute option for which to make the determination. 887 * 888 * @return {@code true} if the provided attribute name has the specified 889 * attribute option, or {@code false} if not. 890 */ 891 public static boolean hasOption(final String name, final String option) 892 { 893 final Set<String> options = getOptions(name); 894 for (final String s : options) 895 { 896 if (s.equalsIgnoreCase(option)) 897 { 898 return true; 899 } 900 } 901 902 return false; 903 } 904 905 906 907 /** 908 * Retrieves the set of options for this attribute. 909 * 910 * @return The set of options for this attribute, or an empty set if there 911 * are none. 912 */ 913 public Set<String> getOptions() 914 { 915 return getOptions(name); 916 } 917 918 919 920 /** 921 * Retrieves the set of options for the provided attribute name. 922 * 923 * @param name The name to be examined. 924 * 925 * @return The set of options for the provided attribute name, or an empty 926 * set if there are none. 927 */ 928 public static Set<String> getOptions(final String name) 929 { 930 int semicolonPos = name.indexOf(';'); 931 if (semicolonPos > 0) 932 { 933 final LinkedHashSet<String> options = new LinkedHashSet<String>(); 934 while (true) 935 { 936 final int nextSemicolonPos = name.indexOf(';', semicolonPos+1); 937 if (nextSemicolonPos > 0) 938 { 939 options.add(name.substring(semicolonPos+1, nextSemicolonPos)); 940 semicolonPos = nextSemicolonPos; 941 } 942 else 943 { 944 options.add(name.substring(semicolonPos+1)); 945 break; 946 } 947 } 948 949 return Collections.unmodifiableSet(options); 950 } 951 else 952 { 953 return Collections.emptySet(); 954 } 955 } 956 957 958 959 /** 960 * Retrieves the matching rule instance used by this attribute. 961 * 962 * @return The matching rule instance used by this attribute. 963 */ 964 public MatchingRule getMatchingRule() 965 { 966 return matchingRule; 967 } 968 969 970 971 /** 972 * Retrieves the value for this attribute as a string. If this attribute has 973 * multiple values, then the first value will be returned. 974 * 975 * @return The value for this attribute, or {@code null} if this attribute 976 * does not have any values. 977 */ 978 public String getValue() 979 { 980 if (values.length == 0) 981 { 982 return null; 983 } 984 985 return values[0].stringValue(); 986 } 987 988 989 990 /** 991 * Retrieves the value for this attribute as a byte array. If this attribute 992 * has multiple values, then the first value will be returned. The returned 993 * array must not be altered by the caller. 994 * 995 * @return The value for this attribute, or {@code null} if this attribute 996 * does not have any values. 997 */ 998 public byte[] getValueByteArray() 999 { 1000 if (values.length == 0) 1001 { 1002 return null; 1003 } 1004 1005 return values[0].getValue(); 1006 } 1007 1008 1009 1010 /** 1011 * Retrieves the value for this attribute as a Boolean. If this attribute has 1012 * multiple values, then the first value will be examined. Values of "true", 1013 * "t", "yes", "y", "on", and "1" will be interpreted as {@code TRUE}. Values 1014 * of "false", "f", "no", "n", "off", and "0" will be interpreted as 1015 * {@code FALSE}. 1016 * 1017 * @return The Boolean value for this attribute, or {@code null} if this 1018 * attribute does not have any values or the value cannot be parsed 1019 * as a Boolean. 1020 */ 1021 public Boolean getValueAsBoolean() 1022 { 1023 if (values.length == 0) 1024 { 1025 return null; 1026 } 1027 1028 final String lowerValue = toLowerCase(values[0].stringValue()); 1029 if (lowerValue.equals("true") || lowerValue.equals("t") || 1030 lowerValue.equals("yes") || lowerValue.equals("y") || 1031 lowerValue.equals("on") || lowerValue.equals("1")) 1032 { 1033 return Boolean.TRUE; 1034 } 1035 else if (lowerValue.equals("false") || lowerValue.equals("f") || 1036 lowerValue.equals("no") || lowerValue.equals("n") || 1037 lowerValue.equals("off") || lowerValue.equals("0")) 1038 { 1039 return Boolean.FALSE; 1040 } 1041 else 1042 { 1043 return null; 1044 } 1045 } 1046 1047 1048 1049 /** 1050 * Retrieves the value for this attribute as a Date, formatted using the 1051 * generalized time syntax. If this attribute has multiple values, then the 1052 * first value will be examined. 1053 * 1054 * @return The Date value for this attribute, or {@code null} if this 1055 * attribute does not have any values or the value cannot be parsed 1056 * as a Date. 1057 */ 1058 public Date getValueAsDate() 1059 { 1060 if (values.length == 0) 1061 { 1062 return null; 1063 } 1064 1065 try 1066 { 1067 return decodeGeneralizedTime(values[0].stringValue()); 1068 } 1069 catch (Exception e) 1070 { 1071 debugException(e); 1072 return null; 1073 } 1074 } 1075 1076 1077 1078 /** 1079 * Retrieves the value for this attribute as a DN. If this attribute has 1080 * multiple values, then the first value will be examined. 1081 * 1082 * @return The DN value for this attribute, or {@code null} if this attribute 1083 * does not have any values or the value cannot be parsed as a DN. 1084 */ 1085 public DN getValueAsDN() 1086 { 1087 if (values.length == 0) 1088 { 1089 return null; 1090 } 1091 1092 try 1093 { 1094 return new DN(values[0].stringValue()); 1095 } 1096 catch (Exception e) 1097 { 1098 debugException(e); 1099 return null; 1100 } 1101 } 1102 1103 1104 1105 /** 1106 * Retrieves the value for this attribute as an Integer. If this attribute 1107 * has multiple values, then the first value will be examined. 1108 * 1109 * @return The Integer value for this attribute, or {@code null} if this 1110 * attribute does not have any values or the value cannot be parsed 1111 * as an Integer. 1112 */ 1113 public Integer getValueAsInteger() 1114 { 1115 if (values.length == 0) 1116 { 1117 return null; 1118 } 1119 1120 try 1121 { 1122 return Integer.valueOf(values[0].stringValue()); 1123 } 1124 catch (NumberFormatException nfe) 1125 { 1126 debugException(nfe); 1127 return null; 1128 } 1129 } 1130 1131 1132 1133 /** 1134 * Retrieves the value for this attribute as a Long. If this attribute has 1135 * multiple values, then the first value will be examined. 1136 * 1137 * @return The Long value for this attribute, or {@code null} if this 1138 * attribute does not have any values or the value cannot be parsed 1139 * as a Long. 1140 */ 1141 public Long getValueAsLong() 1142 { 1143 if (values.length == 0) 1144 { 1145 return null; 1146 } 1147 1148 try 1149 { 1150 return Long.valueOf(values[0].stringValue()); 1151 } 1152 catch (NumberFormatException nfe) 1153 { 1154 debugException(nfe); 1155 return null; 1156 } 1157 } 1158 1159 1160 1161 /** 1162 * Retrieves the set of values for this attribute as strings. The returned 1163 * array must not be altered by the caller. 1164 * 1165 * @return The set of values for this attribute, or an empty array if it does 1166 * not have any values. 1167 */ 1168 public String[] getValues() 1169 { 1170 if (values.length == 0) 1171 { 1172 return NO_STRINGS; 1173 } 1174 1175 final String[] stringValues = new String[values.length]; 1176 for (int i=0; i < values.length; i++) 1177 { 1178 stringValues[i] = values[i].stringValue(); 1179 } 1180 1181 return stringValues; 1182 } 1183 1184 1185 1186 /** 1187 * Retrieves the set of values for this attribute as byte arrays. The 1188 * returned array must not be altered by the caller. 1189 * 1190 * @return The set of values for this attribute, or an empty array if it does 1191 * not have any values. 1192 */ 1193 public byte[][] getValueByteArrays() 1194 { 1195 if (values.length == 0) 1196 { 1197 return NO_BYTE_VALUES; 1198 } 1199 1200 final byte[][] byteValues = new byte[values.length][]; 1201 for (int i=0; i < values.length; i++) 1202 { 1203 byteValues[i] = values[i].getValue(); 1204 } 1205 1206 return byteValues; 1207 } 1208 1209 1210 1211 /** 1212 * Retrieves the set of values for this attribute as an array of ASN.1 octet 1213 * strings. The returned array must not be altered by the caller. 1214 * 1215 * @return The set of values for this attribute as an array of ASN.1 octet 1216 * strings. 1217 */ 1218 public ASN1OctetString[] getRawValues() 1219 { 1220 return values; 1221 } 1222 1223 1224 1225 /** 1226 * Indicates whether this attribute contains at least one value. 1227 * 1228 * @return {@code true} if this attribute has at least one value, or 1229 * {@code false} if not. 1230 */ 1231 public boolean hasValue() 1232 { 1233 return (values.length > 0); 1234 } 1235 1236 1237 1238 /** 1239 * Indicates whether this attribute contains the specified value. 1240 * 1241 * @param value The value for which to make the determination. It must not 1242 * be {@code null}. 1243 * 1244 * @return {@code true} if this attribute has the specified value, or 1245 * {@code false} if not. 1246 */ 1247 public boolean hasValue(final String value) 1248 { 1249 ensureNotNull(value); 1250 1251 return hasValue(new ASN1OctetString(value), matchingRule); 1252 } 1253 1254 1255 1256 /** 1257 * Indicates whether this attribute contains the specified value. 1258 * 1259 * @param value The value for which to make the determination. It 1260 * must not be {@code null}. 1261 * @param matchingRule The matching rule to use when making the 1262 * determination. It must not be {@code null}. 1263 * 1264 * @return {@code true} if this attribute has the specified value, or 1265 * {@code false} if not. 1266 */ 1267 public boolean hasValue(final String value, final MatchingRule matchingRule) 1268 { 1269 ensureNotNull(value); 1270 1271 return hasValue(new ASN1OctetString(value), matchingRule); 1272 } 1273 1274 1275 1276 /** 1277 * Indicates whether this attribute contains the specified value. 1278 * 1279 * @param value The value for which to make the determination. It must not 1280 * be {@code null}. 1281 * 1282 * @return {@code true} if this attribute has the specified value, or 1283 * {@code false} if not. 1284 */ 1285 public boolean hasValue(final byte[] value) 1286 { 1287 ensureNotNull(value); 1288 1289 return hasValue(new ASN1OctetString(value), matchingRule); 1290 } 1291 1292 1293 1294 /** 1295 * Indicates whether this attribute contains the specified value. 1296 * 1297 * @param value The value for which to make the determination. It 1298 * must not be {@code null}. 1299 * @param matchingRule The matching rule to use when making the 1300 * determination. It must not be {@code null}. 1301 * 1302 * @return {@code true} if this attribute has the specified value, or 1303 * {@code false} if not. 1304 */ 1305 public boolean hasValue(final byte[] value, final MatchingRule matchingRule) 1306 { 1307 ensureNotNull(value); 1308 1309 return hasValue(new ASN1OctetString(value), matchingRule); 1310 } 1311 1312 1313 1314 /** 1315 * Indicates whether this attribute contains the specified value. 1316 * 1317 * @param value The value for which to make the determination. 1318 * 1319 * @return {@code true} if this attribute has the specified value, or 1320 * {@code false} if not. 1321 */ 1322 boolean hasValue(final ASN1OctetString value) 1323 { 1324 return hasValue(value, matchingRule); 1325 } 1326 1327 1328 1329 /** 1330 * Indicates whether this attribute contains the specified value. 1331 * 1332 * @param value The value for which to make the determination. It 1333 * must not be {@code null}. 1334 * @param matchingRule The matching rule to use when making the 1335 * determination. It must not be {@code null}. 1336 * 1337 * @return {@code true} if this attribute has the specified value, or 1338 * {@code false} if not. 1339 */ 1340 boolean hasValue(final ASN1OctetString value, final MatchingRule matchingRule) 1341 { 1342 for (final ASN1OctetString existingValue : values) 1343 { 1344 try 1345 { 1346 if (matchingRule.valuesMatch(existingValue, value)) 1347 { 1348 return true; 1349 } 1350 } 1351 catch (final LDAPException le) 1352 { 1353 debugException(le); 1354 1355 // The value cannot be normalized, but we'll still consider it a match 1356 // if the values are exactly the same. 1357 if (existingValue.equals(value)) 1358 { 1359 return true; 1360 } 1361 } 1362 } 1363 1364 // If we've gotten here, then we didn't find a match. 1365 return false; 1366 } 1367 1368 1369 1370 /** 1371 * Retrieves the number of values for this attribute. 1372 * 1373 * @return The number of values for this attribute. 1374 */ 1375 public int size() 1376 { 1377 return values.length; 1378 } 1379 1380 1381 1382 /** 1383 * Writes an ASN.1-encoded representation of this attribute to the provided 1384 * ASN.1 buffer. 1385 * 1386 * @param buffer The ASN.1 buffer to which the encoded representation should 1387 * be written. 1388 */ 1389 public void writeTo(final ASN1Buffer buffer) 1390 { 1391 final ASN1BufferSequence attrSequence = buffer.beginSequence(); 1392 buffer.addOctetString(name); 1393 1394 final ASN1BufferSet valueSet = buffer.beginSet(); 1395 for (final ASN1OctetString value : values) 1396 { 1397 buffer.addElement(value); 1398 } 1399 valueSet.end(); 1400 attrSequence.end(); 1401 } 1402 1403 1404 1405 /** 1406 * Encodes this attribute into a form suitable for use in the LDAP protocol. 1407 * It will be encoded as a sequence containing the attribute name (as an octet 1408 * string) and a set of values. 1409 * 1410 * @return An ASN.1 sequence containing the encoded attribute. 1411 */ 1412 public ASN1Sequence encode() 1413 { 1414 final ASN1Element[] elements = 1415 { 1416 new ASN1OctetString(name), 1417 new ASN1Set(values) 1418 }; 1419 1420 return new ASN1Sequence(elements); 1421 } 1422 1423 1424 1425 /** 1426 * Reads and decodes an attribute from the provided ASN.1 stream reader. 1427 * 1428 * @param reader The ASN.1 stream reader from which to read the attribute. 1429 * 1430 * @return The decoded attribute. 1431 * 1432 * @throws LDAPException If a problem occurs while trying to read or decode 1433 * the attribute. 1434 */ 1435 public static Attribute readFrom(final ASN1StreamReader reader) 1436 throws LDAPException 1437 { 1438 return readFrom(reader, null); 1439 } 1440 1441 1442 1443 /** 1444 * Reads and decodes an attribute from the provided ASN.1 stream reader. 1445 * 1446 * @param reader The ASN.1 stream reader from which to read the attribute. 1447 * @param schema The schema to use to select the appropriate matching rule 1448 * for this attribute. It may be {@code null} if the default 1449 * matching rule should be selected. 1450 * 1451 * @return The decoded attribute. 1452 * 1453 * @throws LDAPException If a problem occurs while trying to read or decode 1454 * the attribute. 1455 */ 1456 public static Attribute readFrom(final ASN1StreamReader reader, 1457 final Schema schema) 1458 throws LDAPException 1459 { 1460 try 1461 { 1462 ensureNotNull(reader.beginSequence()); 1463 final String attrName = reader.readString(); 1464 ensureNotNull(attrName); 1465 1466 final MatchingRule matchingRule = 1467 MatchingRule.selectEqualityMatchingRule(attrName, schema); 1468 1469 final ArrayList<ASN1OctetString> valueList = 1470 new ArrayList<ASN1OctetString>(); 1471 final ASN1StreamReaderSet valueSet = reader.beginSet(); 1472 while (valueSet.hasMoreElements()) 1473 { 1474 valueList.add(new ASN1OctetString(reader.readBytes())); 1475 } 1476 1477 final ASN1OctetString[] values = new ASN1OctetString[valueList.size()]; 1478 valueList.toArray(values); 1479 1480 return new Attribute(attrName, matchingRule, values); 1481 } 1482 catch (Exception e) 1483 { 1484 debugException(e); 1485 throw new LDAPException(ResultCode.DECODING_ERROR, 1486 ERR_ATTR_CANNOT_DECODE.get(getExceptionMessage(e)), e); 1487 } 1488 } 1489 1490 1491 1492 /** 1493 * Decodes the provided ASN.1 sequence as an LDAP attribute. 1494 * 1495 * @param encodedAttribute The ASN.1 sequence to be decoded as an LDAP 1496 * attribute. It must not be {@code null}. 1497 * 1498 * @return The decoded LDAP attribute. 1499 * 1500 * @throws LDAPException If a problem occurs while attempting to decode the 1501 * provided ASN.1 sequence as an LDAP attribute. 1502 */ 1503 public static Attribute decode(final ASN1Sequence encodedAttribute) 1504 throws LDAPException 1505 { 1506 ensureNotNull(encodedAttribute); 1507 1508 final ASN1Element[] elements = encodedAttribute.elements(); 1509 if (elements.length != 2) 1510 { 1511 throw new LDAPException(ResultCode.DECODING_ERROR, 1512 ERR_ATTR_DECODE_INVALID_COUNT.get(elements.length)); 1513 } 1514 1515 final String name = 1516 ASN1OctetString.decodeAsOctetString(elements[0]).stringValue(); 1517 1518 final ASN1Set valueSet; 1519 try 1520 { 1521 valueSet = ASN1Set.decodeAsSet(elements[1]); 1522 } 1523 catch (ASN1Exception ae) 1524 { 1525 debugException(ae); 1526 throw new LDAPException(ResultCode.DECODING_ERROR, 1527 ERR_ATTR_DECODE_VALUE_SET.get(getExceptionMessage(ae)), ae); 1528 } 1529 1530 final ASN1OctetString[] values = 1531 new ASN1OctetString[valueSet.elements().length]; 1532 for (int i=0; i < values.length; i++) 1533 { 1534 values[i] = ASN1OctetString.decodeAsOctetString(valueSet.elements()[i]); 1535 } 1536 1537 return new Attribute(name, CaseIgnoreStringMatchingRule.getInstance(), 1538 values); 1539 } 1540 1541 1542 1543 /** 1544 * Indicates whether any of the values of this attribute need to be 1545 * base64-encoded when represented as LDIF. 1546 * 1547 * @return {@code true} if any of the values of this attribute need to be 1548 * base64-encoded when represented as LDIF, or {@code false} if not. 1549 */ 1550 public boolean needsBase64Encoding() 1551 { 1552 for (final ASN1OctetString v : values) 1553 { 1554 if (needsBase64Encoding(v.getValue())) 1555 { 1556 return true; 1557 } 1558 } 1559 1560 return false; 1561 } 1562 1563 1564 1565 /** 1566 * Indicates whether the provided value needs to be base64-encoded when 1567 * represented as LDIF. 1568 * 1569 * @param v The value for which to make the determination. It must not be 1570 * {@code null}. 1571 * 1572 * @return {@code true} if the provided value needs to be base64-encoded when 1573 * represented as LDIF, or {@code false} if not. 1574 */ 1575 public static boolean needsBase64Encoding(final String v) 1576 { 1577 return needsBase64Encoding(getBytes(v)); 1578 } 1579 1580 1581 1582 /** 1583 * Indicates whether the provided value needs to be base64-encoded when 1584 * represented as LDIF. 1585 * 1586 * @param v The value for which to make the determination. It must not be 1587 * {@code null}. 1588 * 1589 * @return {@code true} if the provided value needs to be base64-encoded when 1590 * represented as LDIF, or {@code false} if not. 1591 */ 1592 public static boolean needsBase64Encoding(final byte[] v) 1593 { 1594 if (v.length == 0) 1595 { 1596 return false; 1597 } 1598 1599 switch (v[0] & 0xFF) 1600 { 1601 case 0x20: // Space 1602 case 0x3A: // Colon 1603 case 0x3C: // Less-than 1604 return true; 1605 } 1606 1607 if ((v[v.length-1] & 0xFF) == 0x20) 1608 { 1609 return true; 1610 } 1611 1612 for (final byte b : v) 1613 { 1614 switch (b & 0xFF) 1615 { 1616 case 0x00: // NULL 1617 case 0x0A: // LF 1618 case 0x0D: // CR 1619 return true; 1620 1621 default: 1622 if ((b & 0x80) != 0x00) 1623 { 1624 return true; 1625 } 1626 break; 1627 } 1628 } 1629 1630 return false; 1631 } 1632 1633 1634 1635 /** 1636 * Generates a hash code for this LDAP attribute. It will be the sum of the 1637 * hash codes for the lowercase attribute name and the normalized values. 1638 * 1639 * @return The generated hash code for this LDAP attribute. 1640 */ 1641 @Override() 1642 public int hashCode() 1643 { 1644 if (hashCode == -1) 1645 { 1646 int c = toLowerCase(name).hashCode(); 1647 1648 for (final ASN1OctetString value : values) 1649 { 1650 try 1651 { 1652 c += matchingRule.normalize(value).hashCode(); 1653 } 1654 catch (LDAPException le) 1655 { 1656 debugException(le); 1657 c += value.hashCode(); 1658 } 1659 } 1660 1661 hashCode = c; 1662 } 1663 1664 return hashCode; 1665 } 1666 1667 1668 1669 /** 1670 * Indicates whether the provided object is equal to this LDAP attribute. The 1671 * object will be considered equal to this LDAP attribute only if it is an 1672 * LDAP attribute with the same name and set of values. 1673 * 1674 * @param o The object for which to make the determination. 1675 * 1676 * @return {@code true} if the provided object may be considered equal to 1677 * this LDAP attribute, or {@code false} if not. 1678 */ 1679 @Override() 1680 public boolean equals(final Object o) 1681 { 1682 if (o == null) 1683 { 1684 return false; 1685 } 1686 1687 if (o == this) 1688 { 1689 return true; 1690 } 1691 1692 if (! (o instanceof Attribute)) 1693 { 1694 return false; 1695 } 1696 1697 final Attribute a = (Attribute) o; 1698 if (! name.equalsIgnoreCase(a.name)) 1699 { 1700 return false; 1701 } 1702 1703 if (values.length != a.values.length) 1704 { 1705 return false; 1706 } 1707 1708 // For a small set of values, we can just iterate through the values of one 1709 // and see if they are all present in the other. However, that can be very 1710 // expensive for a large set of values, so we'll try to go with a more 1711 // efficient approach. 1712 if (values.length > 10) 1713 { 1714 // First, create a hash set containing the un-normalized values of the 1715 // first attribute. 1716 final HashSet<ASN1OctetString> unNormalizedValues = 1717 new HashSet<ASN1OctetString>(values.length); 1718 Collections.addAll(unNormalizedValues, values); 1719 1720 // Next, iterate through the values of the second attribute. For any 1721 // values that exist in the un-normalized set, remove them from that 1722 // set. For any values that aren't in the un-normalized set, create a 1723 // new set with the normalized representations of those values. 1724 HashSet<ASN1OctetString> normalizedMissingValues = null; 1725 for (final ASN1OctetString value : a.values) 1726 { 1727 if (! unNormalizedValues.remove(value)) 1728 { 1729 if (normalizedMissingValues == null) 1730 { 1731 normalizedMissingValues = 1732 new HashSet<ASN1OctetString>(values.length); 1733 } 1734 1735 try 1736 { 1737 normalizedMissingValues.add(matchingRule.normalize(value)); 1738 } 1739 catch (final Exception e) 1740 { 1741 debugException(e); 1742 return false; 1743 } 1744 } 1745 } 1746 1747 // If the un-normalized set is empty, then that means all the values 1748 // exactly match without the need to compare the normalized 1749 // representations. For any values that are left, then we will need to 1750 // compare their normalized representations. 1751 if (normalizedMissingValues != null) 1752 { 1753 for (final ASN1OctetString value : unNormalizedValues) 1754 { 1755 try 1756 { 1757 if (! normalizedMissingValues.contains( 1758 matchingRule.normalize(value))) 1759 { 1760 return false; 1761 } 1762 } 1763 catch (final Exception e) 1764 { 1765 debugException(e); 1766 return false; 1767 } 1768 } 1769 } 1770 } 1771 else 1772 { 1773 for (final ASN1OctetString value : values) 1774 { 1775 if (! a.hasValue(value)) 1776 { 1777 return false; 1778 } 1779 } 1780 } 1781 1782 1783 // If we've gotten here, then we can consider them equal. 1784 return true; 1785 } 1786 1787 1788 1789 /** 1790 * Retrieves a string representation of this LDAP attribute. 1791 * 1792 * @return A string representation of this LDAP attribute. 1793 */ 1794 @Override() 1795 public String toString() 1796 { 1797 final StringBuilder buffer = new StringBuilder(); 1798 toString(buffer); 1799 return buffer.toString(); 1800 } 1801 1802 1803 1804 /** 1805 * Appends a string representation of this LDAP attribute to the provided 1806 * buffer. 1807 * 1808 * @param buffer The buffer to which the string representation of this LDAP 1809 * attribute should be appended. 1810 */ 1811 public void toString(final StringBuilder buffer) 1812 { 1813 buffer.append("Attribute(name="); 1814 buffer.append(name); 1815 1816 if (values.length == 0) 1817 { 1818 buffer.append(", values={"); 1819 } 1820 else if (needsBase64Encoding()) 1821 { 1822 buffer.append(", base64Values={'"); 1823 1824 for (int i=0; i < values.length; i++) 1825 { 1826 if (i > 0) 1827 { 1828 buffer.append("', '"); 1829 } 1830 1831 buffer.append(Base64.encode(values[i].getValue())); 1832 } 1833 1834 buffer.append('\''); 1835 } 1836 else 1837 { 1838 buffer.append(", values={'"); 1839 1840 for (int i=0; i < values.length; i++) 1841 { 1842 if (i > 0) 1843 { 1844 buffer.append("', '"); 1845 } 1846 1847 buffer.append(values[i].stringValue()); 1848 } 1849 1850 buffer.append('\''); 1851 } 1852 1853 buffer.append("})"); 1854 } 1855 }