001/* 002 * Copyright 2007-2014 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2014 UnboundID Corp. 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.schema; 022 023 024 025import java.util.ArrayList; 026import java.util.Collections; 027import java.util.Map; 028import java.util.LinkedHashMap; 029 030import com.unboundid.ldap.sdk.LDAPException; 031import com.unboundid.ldap.sdk.ResultCode; 032import com.unboundid.util.NotMutable; 033import com.unboundid.util.ThreadSafety; 034import com.unboundid.util.ThreadSafetyLevel; 035 036import static com.unboundid.ldap.sdk.schema.SchemaMessages.*; 037import static com.unboundid.util.Debug.*; 038import static com.unboundid.util.StaticUtils.*; 039import static com.unboundid.util.Validator.*; 040 041 042 043/** 044 * This class provides a data structure that describes an LDAP attribute type 045 * schema element. 046 */ 047@NotMutable() 048@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 049public final class AttributeTypeDefinition 050 extends SchemaElement 051{ 052 /** 053 * The serial version UID for this serializable class. 054 */ 055 private static final long serialVersionUID = -6688185196734362719L; 056 057 058 059 // The usage for this attribute type. 060 private final AttributeUsage usage; 061 062 // Indicates whether this attribute type is declared collective. 063 private final boolean isCollective; 064 065 // Indicates whether this attribute type is declared no-user-modification. 066 private final boolean isNoUserModification; 067 068 // Indicates whether this attribute type is declared obsolete. 069 private final boolean isObsolete; 070 071 // Indicates whether this attribute type is declared single-valued. 072 private final boolean isSingleValued; 073 074 // The set of extensions for this attribute type. 075 private final Map<String,String[]> extensions; 076 077 // The string representation of this attribute type. 078 private final String attributeTypeString; 079 080 // The description for this attribute type. 081 private final String description; 082 083 // The name/OID of the equality matching rule for this attribute type. 084 private final String equalityMatchingRule; 085 086 // The OID for this attribute type. 087 private final String oid; 088 089 // The name/OID of the ordering matching rule for this attribute type. 090 private final String orderingMatchingRule; 091 092 // The name/OID of the substring matching rule for this attribute type. 093 private final String substringMatchingRule; 094 095 // The name of the superior type for this attribute type. 096 private final String superiorType; 097 098 // The OID of the syntax for this attribute type. 099 private final String syntaxOID; 100 101 // The set of names for this attribute type. 102 private final String[] names; 103 104 105 106 /** 107 * Creates a new attribute type from the provided string representation. 108 * 109 * @param s The string representation of the attribute type to create, using 110 * the syntax described in RFC 4512 section 4.1.2. It must not be 111 * {@code null}. 112 * 113 * @throws LDAPException If the provided string cannot be decoded as an 114 * attribute type definition. 115 */ 116 public AttributeTypeDefinition(final String s) 117 throws LDAPException 118 { 119 ensureNotNull(s); 120 121 attributeTypeString = s.trim(); 122 123 // The first character must be an opening parenthesis. 124 final int length = attributeTypeString.length(); 125 if (length == 0) 126 { 127 throw new LDAPException(ResultCode.DECODING_ERROR, 128 ERR_ATTRTYPE_DECODE_EMPTY.get()); 129 } 130 else if (attributeTypeString.charAt(0) != '(') 131 { 132 throw new LDAPException(ResultCode.DECODING_ERROR, 133 ERR_ATTRTYPE_DECODE_NO_OPENING_PAREN.get( 134 attributeTypeString)); 135 } 136 137 138 // Skip over any spaces until we reach the start of the OID, then read the 139 // OID until we find the next space. 140 int pos = skipSpaces(attributeTypeString, 1, length); 141 142 StringBuilder buffer = new StringBuilder(); 143 pos = readOID(attributeTypeString, pos, length, buffer); 144 oid = buffer.toString(); 145 146 147 // Technically, attribute type elements are supposed to appear in a specific 148 // order, but we'll be lenient and allow remaining elements to come in any 149 // order. 150 final ArrayList<String> nameList = new ArrayList<String>(1); 151 AttributeUsage attrUsage = null; 152 Boolean collective = null; 153 Boolean noUserMod = null; 154 Boolean obsolete = null; 155 Boolean singleValue = null; 156 final Map<String,String[]> exts = new LinkedHashMap<String,String[]>(); 157 String descr = null; 158 String eqRule = null; 159 String ordRule = null; 160 String subRule = null; 161 String supType = null; 162 String synOID = null; 163 164 while (true) 165 { 166 // Skip over any spaces until we find the next element. 167 pos = skipSpaces(attributeTypeString, pos, length); 168 169 // Read until we find the next space or the end of the string. Use that 170 // token to figure out what to do next. 171 final int tokenStartPos = pos; 172 while ((pos < length) && (attributeTypeString.charAt(pos) != ' ')) 173 { 174 pos++; 175 } 176 177 String token = attributeTypeString.substring(tokenStartPos, pos); 178 179 // It's possible that the token could be smashed right up against the 180 // closing parenthesis. If that's the case, then extract just the token 181 // and handle the closing parenthesis the next time through. 182 if ((token.length() > 1) && (token.endsWith(")"))) 183 { 184 token = token.substring(0, token.length() - 1); 185 pos--; 186 } 187 188 final String lowerToken = toLowerCase(token); 189 if (lowerToken.equals(")")) 190 { 191 // This indicates that we're at the end of the value. There should not 192 // be any more closing characters. 193 if (pos < length) 194 { 195 throw new LDAPException(ResultCode.DECODING_ERROR, 196 ERR_ATTRTYPE_DECODE_CLOSE_NOT_AT_END.get( 197 attributeTypeString)); 198 } 199 break; 200 } 201 else if (lowerToken.equals("name")) 202 { 203 if (nameList.isEmpty()) 204 { 205 pos = skipSpaces(attributeTypeString, pos, length); 206 pos = readQDStrings(attributeTypeString, pos, length, nameList); 207 } 208 else 209 { 210 throw new LDAPException(ResultCode.DECODING_ERROR, 211 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 212 attributeTypeString, "NAME")); 213 } 214 } 215 else if (lowerToken.equals("desc")) 216 { 217 if (descr == null) 218 { 219 pos = skipSpaces(attributeTypeString, pos, length); 220 221 buffer = new StringBuilder(); 222 pos = readQDString(attributeTypeString, pos, length, buffer); 223 descr = buffer.toString(); 224 } 225 else 226 { 227 throw new LDAPException(ResultCode.DECODING_ERROR, 228 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 229 attributeTypeString, "DESC")); 230 } 231 } 232 else if (lowerToken.equals("obsolete")) 233 { 234 if (obsolete == null) 235 { 236 obsolete = true; 237 } 238 else 239 { 240 throw new LDAPException(ResultCode.DECODING_ERROR, 241 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 242 attributeTypeString, "OBSOLETE")); 243 } 244 } 245 else if (lowerToken.equals("sup")) 246 { 247 if (supType == null) 248 { 249 pos = skipSpaces(attributeTypeString, pos, length); 250 251 buffer = new StringBuilder(); 252 pos = readOID(attributeTypeString, pos, length, buffer); 253 supType = buffer.toString(); 254 } 255 else 256 { 257 throw new LDAPException(ResultCode.DECODING_ERROR, 258 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 259 attributeTypeString, "SUP")); 260 } 261 } 262 else if (lowerToken.equals("equality")) 263 { 264 if (eqRule == null) 265 { 266 pos = skipSpaces(attributeTypeString, pos, length); 267 268 buffer = new StringBuilder(); 269 pos = readOID(attributeTypeString, pos, length, buffer); 270 eqRule = buffer.toString(); 271 } 272 else 273 { 274 throw new LDAPException(ResultCode.DECODING_ERROR, 275 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 276 attributeTypeString, "EQUALITY")); 277 } 278 } 279 else if (lowerToken.equals("ordering")) 280 { 281 if (ordRule == null) 282 { 283 pos = skipSpaces(attributeTypeString, pos, length); 284 285 buffer = new StringBuilder(); 286 pos = readOID(attributeTypeString, pos, length, buffer); 287 ordRule = buffer.toString(); 288 } 289 else 290 { 291 throw new LDAPException(ResultCode.DECODING_ERROR, 292 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 293 attributeTypeString, "ORDERING")); 294 } 295 } 296 else if (lowerToken.equals("substr")) 297 { 298 if (subRule == null) 299 { 300 pos = skipSpaces(attributeTypeString, pos, length); 301 302 buffer = new StringBuilder(); 303 pos = readOID(attributeTypeString, pos, length, buffer); 304 subRule = buffer.toString(); 305 } 306 else 307 { 308 throw new LDAPException(ResultCode.DECODING_ERROR, 309 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 310 attributeTypeString, "SUBSTR")); 311 } 312 } 313 else if (lowerToken.equals("syntax")) 314 { 315 if (synOID == null) 316 { 317 pos = skipSpaces(attributeTypeString, pos, length); 318 319 buffer = new StringBuilder(); 320 pos = readOID(attributeTypeString, pos, length, buffer); 321 synOID = buffer.toString(); 322 } 323 else 324 { 325 throw new LDAPException(ResultCode.DECODING_ERROR, 326 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 327 attributeTypeString, "SYNTAX")); 328 } 329 } 330 else if (lowerToken.equals("single-value")) 331 { 332 if (singleValue == null) 333 { 334 singleValue = true; 335 } 336 else 337 { 338 throw new LDAPException(ResultCode.DECODING_ERROR, 339 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 340 attributeTypeString, "SINGLE-VALUE")); 341 } 342 } 343 else if (lowerToken.equals("collective")) 344 { 345 if (collective == null) 346 { 347 collective = true; 348 } 349 else 350 { 351 throw new LDAPException(ResultCode.DECODING_ERROR, 352 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 353 attributeTypeString, "COLLECTIVE")); 354 } 355 } 356 else if (lowerToken.equals("no-user-modification")) 357 { 358 if (noUserMod == null) 359 { 360 noUserMod = true; 361 } 362 else 363 { 364 throw new LDAPException(ResultCode.DECODING_ERROR, 365 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 366 attributeTypeString, 367 "NO-USER-MODIFICATION")); 368 } 369 } 370 else if (lowerToken.equals("usage")) 371 { 372 if (attrUsage == null) 373 { 374 pos = skipSpaces(attributeTypeString, pos, length); 375 376 buffer = new StringBuilder(); 377 pos = readOID(attributeTypeString, pos, length, buffer); 378 379 final String usageStr = toLowerCase(buffer.toString()); 380 if (usageStr.equals("userapplications")) 381 { 382 attrUsage = AttributeUsage.USER_APPLICATIONS; 383 } 384 else if (usageStr.equals("directoryoperation")) 385 { 386 attrUsage = AttributeUsage.DIRECTORY_OPERATION; 387 } 388 else if (usageStr.equals("distributedoperation")) 389 { 390 attrUsage = AttributeUsage.DISTRIBUTED_OPERATION; 391 } 392 else if (usageStr.equals("dsaoperation")) 393 { 394 attrUsage = AttributeUsage.DSA_OPERATION; 395 } 396 else 397 { 398 throw new LDAPException(ResultCode.DECODING_ERROR, 399 ERR_ATTRTYPE_DECODE_INVALID_USAGE.get( 400 attributeTypeString, usageStr)); 401 } 402 } 403 else 404 { 405 throw new LDAPException(ResultCode.DECODING_ERROR, 406 ERR_ATTRTYPE_DECODE_MULTIPLE_ELEMENTS.get( 407 attributeTypeString, "USAGE")); 408 } 409 } 410 else if (lowerToken.startsWith("x-")) 411 { 412 pos = skipSpaces(attributeTypeString, pos, length); 413 414 final ArrayList<String> valueList = new ArrayList<String>(); 415 pos = readQDStrings(attributeTypeString, pos, length, valueList); 416 417 final String[] values = new String[valueList.size()]; 418 valueList.toArray(values); 419 420 if (exts.containsKey(token)) 421 { 422 throw new LDAPException(ResultCode.DECODING_ERROR, 423 ERR_ATTRTYPE_DECODE_DUP_EXT.get( 424 attributeTypeString, token)); 425 } 426 427 exts.put(token, values); 428 } 429 else 430 { 431 throw new LDAPException(ResultCode.DECODING_ERROR, 432 ERR_ATTRTYPE_DECODE_UNEXPECTED_TOKEN.get( 433 attributeTypeString, token)); 434 } 435 } 436 437 description = descr; 438 equalityMatchingRule = eqRule; 439 orderingMatchingRule = ordRule; 440 substringMatchingRule = subRule; 441 superiorType = supType; 442 syntaxOID = synOID; 443 444 names = new String[nameList.size()]; 445 nameList.toArray(names); 446 447 isObsolete = (obsolete != null); 448 isSingleValued = (singleValue != null); 449 isCollective = (collective != null); 450 isNoUserModification = (noUserMod != null); 451 452 if (attrUsage == null) 453 { 454 usage = AttributeUsage.USER_APPLICATIONS; 455 } 456 else 457 { 458 usage = attrUsage; 459 } 460 461 extensions = Collections.unmodifiableMap(exts); 462 } 463 464 465 466 /** 467 * Creates a new attribute type with the provided information. 468 * 469 * @param oid The OID for this attribute type. It must 470 * not be {@code null}. 471 * @param names The set of names for this attribute type. 472 * It may be {@code null} or empty if the 473 * attribute type should only be referenced by 474 * OID. 475 * @param description The description for this attribute type. It 476 * may be {@code null} if there is no 477 * description. 478 * @param isObsolete Indicates whether this attribute type is 479 * declared obsolete. 480 * @param superiorType The name or OID of the superior attribute 481 * type. It may be {@code null} if there is no 482 * superior type. 483 * @param equalityMatchingRule The name or OID of the equality matching 484 * rule for this attribute type. It may be 485 * {@code null} if a default rule is to be 486 * inherited. 487 * @param orderingMatchingRule The name or OID of the ordering matching 488 * rule for this attribute type. It may be 489 * {@code null} if a default rule is to be 490 * inherited. 491 * @param substringMatchingRule The name or OID of the substring matching 492 * rule for this attribute type. It may be 493 * {@code null} if a default rule is to be 494 * inherited. 495 * @param syntaxOID The syntax OID for this attribute type. It 496 * may be {@code null} if a default syntax is 497 * to be inherited. 498 * @param isSingleValued Indicates whether attributes of this type 499 * are only allowed to have a single value. 500 * @param isCollective Indicates whether this attribute type should 501 * be considered collective. 502 * @param isNoUserModification Indicates whether clients should be allowed 503 * to modify attributes of this type. 504 * @param usage The attribute usage for this attribute type. 505 * It may be {@code null} if the default usage 506 * of userApplications is to be used. 507 * @param extensions The set of extensions for this attribute 508 * type. It may be {@code null} or empty if 509 * there should not be any extensions. 510 */ 511 public AttributeTypeDefinition(final String oid, final String[] names, 512 final String description, 513 final boolean isObsolete, 514 final String superiorType, 515 final String equalityMatchingRule, 516 final String orderingMatchingRule, 517 final String substringMatchingRule, 518 final String syntaxOID, 519 final boolean isSingleValued, 520 final boolean isCollective, 521 final boolean isNoUserModification, 522 final AttributeUsage usage, 523 final Map<String,String[]> extensions) 524 { 525 ensureNotNull(oid); 526 527 this.oid = oid; 528 this.description = description; 529 this.isObsolete = isObsolete; 530 this.superiorType = superiorType; 531 this.equalityMatchingRule = equalityMatchingRule; 532 this.orderingMatchingRule = orderingMatchingRule; 533 this.substringMatchingRule = substringMatchingRule; 534 this.syntaxOID = syntaxOID; 535 this.isSingleValued = isSingleValued; 536 this.isCollective = isCollective; 537 this.isNoUserModification = isNoUserModification; 538 539 if (names == null) 540 { 541 this.names = NO_STRINGS; 542 } 543 else 544 { 545 this.names = names; 546 } 547 548 if (usage == null) 549 { 550 this.usage = AttributeUsage.USER_APPLICATIONS; 551 } 552 else 553 { 554 this.usage = usage; 555 } 556 557 if (extensions == null) 558 { 559 this.extensions = Collections.emptyMap(); 560 } 561 else 562 { 563 this.extensions = Collections.unmodifiableMap(extensions); 564 } 565 566 final StringBuilder buffer = new StringBuilder(); 567 createDefinitionString(buffer); 568 attributeTypeString = buffer.toString(); 569 } 570 571 572 573 /** 574 * Constructs a string representation of this attribute type definition in the 575 * provided buffer. 576 * 577 * @param buffer The buffer in which to construct a string representation of 578 * this attribute type definition. 579 */ 580 private void createDefinitionString(final StringBuilder buffer) 581 { 582 buffer.append("( "); 583 buffer.append(oid); 584 585 if (names.length == 1) 586 { 587 buffer.append(" NAME '"); 588 buffer.append(names[0]); 589 buffer.append('\''); 590 } 591 else if (names.length > 1) 592 { 593 buffer.append(" NAME ("); 594 for (final String name : names) 595 { 596 buffer.append(" '"); 597 buffer.append(name); 598 buffer.append('\''); 599 } 600 buffer.append(" )"); 601 } 602 603 if (description != null) 604 { 605 buffer.append(" DESC '"); 606 encodeValue(description, buffer); 607 buffer.append('\''); 608 } 609 610 if (isObsolete) 611 { 612 buffer.append(" OBSOLETE"); 613 } 614 615 if (superiorType != null) 616 { 617 buffer.append(" SUP "); 618 buffer.append(superiorType); 619 } 620 621 if (equalityMatchingRule != null) 622 { 623 buffer.append(" EQUALITY "); 624 buffer.append(equalityMatchingRule); 625 } 626 627 if (orderingMatchingRule != null) 628 { 629 buffer.append(" ORDERING "); 630 buffer.append(orderingMatchingRule); 631 } 632 633 if (substringMatchingRule != null) 634 { 635 buffer.append(" SUBSTR "); 636 buffer.append(substringMatchingRule); 637 } 638 639 if (syntaxOID != null) 640 { 641 buffer.append(" SYNTAX "); 642 buffer.append(syntaxOID); 643 } 644 645 if (isSingleValued) 646 { 647 buffer.append(" SINGLE-VALUE"); 648 } 649 650 if (isCollective) 651 { 652 buffer.append(" COLLECTIVE"); 653 } 654 655 if (isNoUserModification) 656 { 657 buffer.append(" NO-USER-MODIFICATION"); 658 } 659 660 buffer.append(" USAGE "); 661 buffer.append(usage.getName()); 662 663 for (final Map.Entry<String,String[]> e : extensions.entrySet()) 664 { 665 final String name = e.getKey(); 666 final String[] values = e.getValue(); 667 if (values.length == 1) 668 { 669 buffer.append(' '); 670 buffer.append(name); 671 buffer.append(" '"); 672 encodeValue(values[0], buffer); 673 buffer.append('\''); 674 } 675 else 676 { 677 buffer.append(' '); 678 buffer.append(name); 679 buffer.append(" ("); 680 for (final String value : values) 681 { 682 buffer.append(" '"); 683 encodeValue(value, buffer); 684 buffer.append('\''); 685 } 686 buffer.append(" )"); 687 } 688 } 689 690 buffer.append(" )"); 691 } 692 693 694 695 /** 696 * Retrieves the OID for this attribute type. 697 * 698 * @return The OID for this attribute type. 699 */ 700 public String getOID() 701 { 702 return oid; 703 } 704 705 706 707 /** 708 * Retrieves the set of names for this attribute type. 709 * 710 * @return The set of names for this attribute type, or an empty array if it 711 * does not have any names. 712 */ 713 public String[] getNames() 714 { 715 return names; 716 } 717 718 719 720 /** 721 * Retrieves the primary name that can be used to reference this attribute 722 * type. If one or more names are defined, then the first name will be used. 723 * Otherwise, the OID will be returned. 724 * 725 * @return The primary name that can be used to reference this attribute 726 * type. 727 */ 728 public String getNameOrOID() 729 { 730 if (names.length == 0) 731 { 732 return oid; 733 } 734 else 735 { 736 return names[0]; 737 } 738 } 739 740 741 742 /** 743 * Indicates whether the provided string matches the OID or any of the names 744 * for this attribute type. 745 * 746 * @param s The string for which to make the determination. It must not be 747 * {@code null}. 748 * 749 * @return {@code true} if the provided string matches the OID or any of the 750 * names for this attribute type, or {@code false} if not. 751 */ 752 public boolean hasNameOrOID(final String s) 753 { 754 for (final String name : names) 755 { 756 if (s.equalsIgnoreCase(name)) 757 { 758 return true; 759 } 760 } 761 762 return s.equalsIgnoreCase(oid); 763 } 764 765 766 767 /** 768 * Retrieves the description for this attribute type, if available. 769 * 770 * @return The description for this attribute type, or {@code null} if there 771 * is no description defined. 772 */ 773 public String getDescription() 774 { 775 return description; 776 } 777 778 779 780 /** 781 * Indicates whether this attribute type is declared obsolete. 782 * 783 * @return {@code true} if this attribute type is declared obsolete, or 784 * {@code false} if it is not. 785 */ 786 public boolean isObsolete() 787 { 788 return isObsolete; 789 } 790 791 792 793 /** 794 * Retrieves the name or OID of the superior type for this attribute type, if 795 * available. 796 * 797 * @return The name or OID of the superior type for this attribute type, or 798 * {@code null} if no superior type is defined. 799 */ 800 public String getSuperiorType() 801 { 802 return superiorType; 803 } 804 805 806 807 /** 808 * Retrieves the superior attribute type definition for this attribute type, 809 * if available. 810 * 811 * @param schema The schema to use to get the superior attribute type. 812 * 813 * @return The superior attribute type definition for this attribute type, or 814 * {@code null} if no superior type is defined, or if the superior 815 * type is not included in the provided schema. 816 */ 817 public AttributeTypeDefinition getSuperiorType(final Schema schema) 818 { 819 if (superiorType != null) 820 { 821 return schema.getAttributeType(superiorType); 822 } 823 824 return null; 825 } 826 827 828 829 /** 830 * Retrieves the name or OID of the equality matching rule for this attribute 831 * type, if available. 832 * 833 * @return The name or OID of the equality matching rule for this attribute 834 * type, or {@code null} if no equality matching rule is defined or a 835 * default rule will be inherited. 836 */ 837 public String getEqualityMatchingRule() 838 { 839 return equalityMatchingRule; 840 } 841 842 843 844 /** 845 * Retrieves the name or OID of the equality matching rule for this attribute 846 * type, examining superior attribute types if necessary. 847 * 848 * @param schema The schema to use to get the superior attribute type. 849 * 850 * @return The name or OID of the equality matching rule for this attribute 851 * type, or {@code null} if no equality matching rule is defined. 852 */ 853 public String getEqualityMatchingRule(final Schema schema) 854 { 855 if (equalityMatchingRule == null) 856 { 857 final AttributeTypeDefinition sup = getSuperiorType(schema); 858 if (sup != null) 859 { 860 return sup.getEqualityMatchingRule(schema); 861 } 862 } 863 864 return equalityMatchingRule; 865 } 866 867 868 869 /** 870 * Retrieves the name or OID of the ordering matching rule for this attribute 871 * type, if available. 872 * 873 * @return The name or OID of the ordering matching rule for this attribute 874 * type, or {@code null} if no ordering matching rule is defined or a 875 * default rule will be inherited. 876 */ 877 public String getOrderingMatchingRule() 878 { 879 return orderingMatchingRule; 880 } 881 882 883 884 /** 885 * Retrieves the name or OID of the ordering matching rule for this attribute 886 * type, examining superior attribute types if necessary. 887 * 888 * @param schema The schema to use to get the superior attribute type. 889 * 890 * @return The name or OID of the ordering matching rule for this attribute 891 * type, or {@code null} if no ordering matching rule is defined. 892 */ 893 public String getOrderingMatchingRule(final Schema schema) 894 { 895 if (orderingMatchingRule == null) 896 { 897 final AttributeTypeDefinition sup = getSuperiorType(schema); 898 if (sup != null) 899 { 900 return sup.getOrderingMatchingRule(schema); 901 } 902 } 903 904 return orderingMatchingRule; 905 } 906 907 908 909 /** 910 * Retrieves the name or OID of the substring matching rule for this attribute 911 * type, if available. 912 * 913 * @return The name or OID of the substring matching rule for this attribute 914 * type, or {@code null} if no substring matching rule is defined or 915 * a default rule will be inherited. 916 */ 917 public String getSubstringMatchingRule() 918 { 919 return substringMatchingRule; 920 } 921 922 923 924 /** 925 * Retrieves the name or OID of the substring matching rule for this attribute 926 * type, examining superior attribute types if necessary. 927 * 928 * @param schema The schema to use to get the superior attribute type. 929 * 930 * @return The name or OID of the substring matching rule for this attribute 931 * type, or {@code null} if no substring matching rule is defined. 932 */ 933 public String getSubstringMatchingRule(final Schema schema) 934 { 935 if (substringMatchingRule == null) 936 { 937 final AttributeTypeDefinition sup = getSuperiorType(schema); 938 if (sup != null) 939 { 940 return sup.getSubstringMatchingRule(schema); 941 } 942 } 943 944 return substringMatchingRule; 945 } 946 947 948 949 /** 950 * Retrieves the OID of the syntax for this attribute type, if available. It 951 * may optionally include a minimum upper bound in curly braces. 952 * 953 * @return The OID of the syntax for this attribute type, or {@code null} if 954 * the syntax will be inherited. 955 */ 956 public String getSyntaxOID() 957 { 958 return syntaxOID; 959 } 960 961 962 963 /** 964 * Retrieves the OID of the syntax for this attribute type, examining superior 965 * types if necessary. It may optionally include a minimum upper bound in 966 * curly braces. 967 * 968 * @param schema The schema to use to get the superior attribute type. 969 * 970 * @return The OID of the syntax for this attribute type, or {@code null} if 971 * no syntax is defined. 972 */ 973 public String getSyntaxOID(final Schema schema) 974 { 975 if (syntaxOID == null) 976 { 977 final AttributeTypeDefinition sup = getSuperiorType(schema); 978 if (sup != null) 979 { 980 return sup.getSyntaxOID(schema); 981 } 982 } 983 984 return syntaxOID; 985 } 986 987 988 989 /** 990 * Retrieves the OID of the syntax for this attribute type, if available. If 991 * the attribute type definition includes a minimum upper bound in curly 992 * braces, it will be removed from the value that is returned. 993 * 994 * @return The OID of the syntax for this attribute type, or {@code null} if 995 * the syntax will be inherited. 996 */ 997 public String getBaseSyntaxOID() 998 { 999 return getBaseSyntaxOID(syntaxOID); 1000 } 1001 1002 1003 1004 /** 1005 * Retrieves the base OID of the syntax for this attribute type, examining 1006 * superior types if necessary. If the attribute type definition includes a 1007 * minimum upper bound in curly braces, it will be removed from the value that 1008 * is returned. 1009 * 1010 * @param schema The schema to use to get the superior attribute type, if 1011 * necessary. 1012 * 1013 * @return The OID of the syntax for this attribute type, or {@code null} if 1014 * no syntax is defined. 1015 */ 1016 public String getBaseSyntaxOID(final Schema schema) 1017 { 1018 return getBaseSyntaxOID(getSyntaxOID(schema)); 1019 } 1020 1021 1022 1023 /** 1024 * Retrieves the base OID of the syntax for this attribute type, examining 1025 * superior types if necessary. If the attribute type definition includes a 1026 * minimum upper bound in curly braces, it will be removed from the value that 1027 * is returned. 1028 * 1029 * @param syntaxOID The syntax OID (optionally including the minimum upper 1030 * bound element) to examine. 1031 * 1032 * @return The OID of the syntax for this attribute type, or {@code null} if 1033 * no syntax is defined. 1034 */ 1035 public static String getBaseSyntaxOID(final String syntaxOID) 1036 { 1037 if (syntaxOID == null) 1038 { 1039 return null; 1040 } 1041 1042 final int curlyPos = syntaxOID.indexOf('{'); 1043 if (curlyPos > 0) 1044 { 1045 return syntaxOID.substring(0, curlyPos); 1046 } 1047 else 1048 { 1049 return syntaxOID; 1050 } 1051 } 1052 1053 1054 1055 /** 1056 * Retrieves the value of the minimum upper bound element of the syntax 1057 * definition for this attribute type, if defined. If a minimum upper bound 1058 * is present (as signified by an integer value in curly braces immediately 1059 * following the syntax OID without any space between them), then it should 1060 * serve as an indication to the directory server that it should be prepared 1061 * to handle values with at least that number of (possibly multi-byte) 1062 * characters. 1063 * 1064 * @return The value of the minimum upper bound element of the syntax 1065 * definition for this attribute type, or -1 if no syntax is defined 1066 * defined or if it does not have a valid minimum upper bound. 1067 */ 1068 public int getSyntaxMinimumUpperBound() 1069 { 1070 return getSyntaxMinimumUpperBound(syntaxOID); 1071 } 1072 1073 1074 1075 /** 1076 * Retrieves the value of the minimum upper bound element of the syntax 1077 * definition for this attribute type, if defined. If a minimum upper bound 1078 * is present (as signified by an integer value in curly braces immediately 1079 * following the syntax OID without any space between them), then it should 1080 * serve as an indication to the directory server that it should be prepared 1081 * to handle values with at least that number of (possibly multi-byte) 1082 * characters. 1083 * 1084 * @param schema The schema to use to get the superior attribute type, if 1085 * necessary. 1086 * 1087 * @return The value of the minimum upper bound element of the syntax 1088 * definition for this attribute type, or -1 if no syntax is defined 1089 * defined or if it does not have a valid minimum upper bound. 1090 */ 1091 public int getSyntaxMinimumUpperBound(final Schema schema) 1092 { 1093 return getSyntaxMinimumUpperBound(getSyntaxOID(schema)); 1094 } 1095 1096 1097 1098 /** 1099 * Retrieves the value of the minimum upper bound element of the syntax 1100 * definition for this attribute type, if defined. If a minimum upper bound 1101 * is present (as signified by an integer value in curly braces immediately 1102 * following the syntax OID without any space between them), then it should 1103 * serve as an indication to the directory server that it should be prepared 1104 * to handle values with at least that number of (possibly multi-byte) 1105 * characters. 1106 * 1107 * @param syntaxOID The syntax OID (optionally including the minimum upper 1108 * bound element) to examine. 1109 * 1110 * @return The value of the minimum upper bound element of the provided 1111 * syntax OID, or -1 if the provided syntax OID is {@code null} or 1112 * does not have a valid minimum upper bound. 1113 */ 1114 public static int getSyntaxMinimumUpperBound(final String syntaxOID) 1115 { 1116 if (syntaxOID == null) 1117 { 1118 return -1; 1119 } 1120 1121 final int curlyPos = syntaxOID.indexOf('{'); 1122 if ((curlyPos > 0) && syntaxOID.endsWith("}")) 1123 { 1124 try 1125 { 1126 return Integer.parseInt(syntaxOID.substring(curlyPos+1, 1127 syntaxOID.length()-1)); 1128 } 1129 catch (final Exception e) 1130 { 1131 debugException(e); 1132 return -1; 1133 } 1134 } 1135 else 1136 { 1137 return -1; 1138 } 1139 } 1140 1141 1142 1143 /** 1144 * Indicates whether this attribute type is declared single-valued, and 1145 * therefore attributes of this type will only be allowed to have at most one 1146 * value. 1147 * 1148 * @return {@code true} if this attribute type is declared single-valued, or 1149 * {@code false} if not. 1150 */ 1151 public boolean isSingleValued() 1152 { 1153 return isSingleValued; 1154 } 1155 1156 1157 1158 /** 1159 * Indicates whether this attribute type is declared collective, and therefore 1160 * values may be dynamically generated as described in RFC 3671. 1161 * 1162 * @return {@code true} if this attribute type is declared collective, or 1163 * {@code false} if not. 1164 */ 1165 public boolean isCollective() 1166 { 1167 return isCollective; 1168 } 1169 1170 1171 1172 /** 1173 * Indicates whether this attribute type is declared no-user-modification, 1174 * and therefore attributes of this type will not be allowed to be altered 1175 * by clients. 1176 * 1177 * @return {@code true} if this attribute type is declared 1178 * no-user-modification, or {@code false} if not. 1179 */ 1180 public boolean isNoUserModification() 1181 { 1182 return isNoUserModification; 1183 } 1184 1185 1186 1187 /** 1188 * Retrieves the attribute usage for this attribute type. 1189 * 1190 * @return The attribute usage for this attribute type. 1191 */ 1192 public AttributeUsage getUsage() 1193 { 1194 return usage; 1195 } 1196 1197 1198 1199 /** 1200 * Indicates whether this attribute type has an operational attribute usage. 1201 * 1202 * @return {@code true} if this attribute type has an operational attribute 1203 * usage, or {@code false} if not. 1204 */ 1205 public boolean isOperational() 1206 { 1207 return usage.isOperational(); 1208 } 1209 1210 1211 1212 /** 1213 * Retrieves the set of extensions for this attribute type. They will be 1214 * mapped from the extension name (which should start with "X-") to the set of 1215 * values for that extension. 1216 * 1217 * @return The set of extensions for this attribute type. 1218 */ 1219 public Map<String,String[]> getExtensions() 1220 { 1221 return extensions; 1222 } 1223 1224 1225 1226 /** 1227 * {@inheritDoc} 1228 */ 1229 @Override() 1230 public int hashCode() 1231 { 1232 return oid.hashCode(); 1233 } 1234 1235 1236 1237 /** 1238 * {@inheritDoc} 1239 */ 1240 @Override() 1241 public boolean equals(final Object o) 1242 { 1243 if (o == null) 1244 { 1245 return false; 1246 } 1247 1248 if (o == this) 1249 { 1250 return true; 1251 } 1252 1253 if (! (o instanceof AttributeTypeDefinition)) 1254 { 1255 return false; 1256 } 1257 1258 final AttributeTypeDefinition d = (AttributeTypeDefinition) o; 1259 return(oid.equals(d.oid) && 1260 stringsEqualIgnoreCaseOrderIndependent(names, d.names) && 1261 bothNullOrEqual(usage, d.usage) && 1262 bothNullOrEqualIgnoreCase(description, d.description) && 1263 bothNullOrEqualIgnoreCase(equalityMatchingRule, 1264 d.equalityMatchingRule) && 1265 bothNullOrEqualIgnoreCase(orderingMatchingRule, 1266 d.orderingMatchingRule) && 1267 bothNullOrEqualIgnoreCase(substringMatchingRule, 1268 d.substringMatchingRule) && 1269 bothNullOrEqualIgnoreCase(superiorType, d.superiorType) && 1270 bothNullOrEqualIgnoreCase(syntaxOID, d.syntaxOID) && 1271 (isCollective == d.isCollective) && 1272 (isNoUserModification == d.isNoUserModification) && 1273 (isObsolete == d.isObsolete) && 1274 (isSingleValued == d.isSingleValued) && 1275 extensionsEqual(extensions, d.extensions)); 1276 } 1277 1278 1279 1280 /** 1281 * Retrieves a string representation of this attribute type definition, in the 1282 * format described in RFC 4512 section 4.1.2. 1283 * 1284 * @return A string representation of this attribute type definition. 1285 */ 1286 @Override() 1287 public String toString() 1288 { 1289 return attributeTypeString; 1290 } 1291}