001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 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 name The name for this attribute type. It may be 472 * {@code null} if the attribute type should 473 * only be referenced by OID. 474 * @param description The description for this attribute type. It 475 * may be {@code null} if there is no 476 * description. 477 * @param equalityMatchingRule The name or OID of the equality matching 478 * rule for this attribute type. It may be 479 * {@code null} if a default rule is to be 480 * inherited. 481 * @param orderingMatchingRule The name or OID of the ordering matching 482 * rule for this attribute type. It may be 483 * {@code null} if a default rule is to be 484 * inherited. 485 * @param substringMatchingRule The name or OID of the substring matching 486 * rule for this attribute type. It may be 487 * {@code null} if a default rule is to be 488 * inherited. 489 * @param syntaxOID The syntax OID for this attribute type. It 490 * may be {@code null} if a default syntax is 491 * to be inherited. 492 * @param isSingleValued Indicates whether attributes of this type 493 * are only allowed to have a single value. 494 * @param extensions The set of extensions for this attribute 495 * type. It may be {@code null} or empty if 496 * there should not be any extensions. 497 */ 498 public AttributeTypeDefinition(final String oid, final String name, 499 final String description, 500 final String equalityMatchingRule, 501 final String orderingMatchingRule, 502 final String substringMatchingRule, 503 final String syntaxOID, 504 final boolean isSingleValued, 505 final Map<String,String[]> extensions) 506 { 507 this(oid, ((name == null) ? null : new String[] { name }), description, 508 false, null, equalityMatchingRule, orderingMatchingRule, 509 substringMatchingRule, syntaxOID, isSingleValued, false, false, 510 AttributeUsage.USER_APPLICATIONS, extensions); 511 } 512 513 514 515 /** 516 * Creates a new attribute type with the provided information. 517 * 518 * @param oid The OID for this attribute type. It must 519 * not be {@code null}. 520 * @param names The set of names for this attribute type. 521 * It may be {@code null} or empty if the 522 * attribute type should only be referenced by 523 * OID. 524 * @param description The description for this attribute type. It 525 * may be {@code null} if there is no 526 * description. 527 * @param isObsolete Indicates whether this attribute type is 528 * declared obsolete. 529 * @param superiorType The name or OID of the superior attribute 530 * type. It may be {@code null} if there is no 531 * superior type. 532 * @param equalityMatchingRule The name or OID of the equality matching 533 * rule for this attribute type. It may be 534 * {@code null} if a default rule is to be 535 * inherited. 536 * @param orderingMatchingRule The name or OID of the ordering matching 537 * rule for this attribute type. It may be 538 * {@code null} if a default rule is to be 539 * inherited. 540 * @param substringMatchingRule The name or OID of the substring matching 541 * rule for this attribute type. It may be 542 * {@code null} if a default rule is to be 543 * inherited. 544 * @param syntaxOID The syntax OID for this attribute type. It 545 * may be {@code null} if a default syntax is 546 * to be inherited. 547 * @param isSingleValued Indicates whether attributes of this type 548 * are only allowed to have a single value. 549 * @param isCollective Indicates whether this attribute type should 550 * be considered collective. 551 * @param isNoUserModification Indicates whether clients should be allowed 552 * to modify attributes of this type. 553 * @param usage The attribute usage for this attribute type. 554 * It may be {@code null} if the default usage 555 * of userApplications is to be used. 556 * @param extensions The set of extensions for this attribute 557 * type. It may be {@code null} or empty if 558 * there should not be any extensions. 559 */ 560 public AttributeTypeDefinition(final String oid, final String[] names, 561 final String description, 562 final boolean isObsolete, 563 final String superiorType, 564 final String equalityMatchingRule, 565 final String orderingMatchingRule, 566 final String substringMatchingRule, 567 final String syntaxOID, 568 final boolean isSingleValued, 569 final boolean isCollective, 570 final boolean isNoUserModification, 571 final AttributeUsage usage, 572 final Map<String,String[]> extensions) 573 { 574 ensureNotNull(oid); 575 576 this.oid = oid; 577 this.description = description; 578 this.isObsolete = isObsolete; 579 this.superiorType = superiorType; 580 this.equalityMatchingRule = equalityMatchingRule; 581 this.orderingMatchingRule = orderingMatchingRule; 582 this.substringMatchingRule = substringMatchingRule; 583 this.syntaxOID = syntaxOID; 584 this.isSingleValued = isSingleValued; 585 this.isCollective = isCollective; 586 this.isNoUserModification = isNoUserModification; 587 588 if (names == null) 589 { 590 this.names = NO_STRINGS; 591 } 592 else 593 { 594 this.names = names; 595 } 596 597 if (usage == null) 598 { 599 this.usage = AttributeUsage.USER_APPLICATIONS; 600 } 601 else 602 { 603 this.usage = usage; 604 } 605 606 if (extensions == null) 607 { 608 this.extensions = Collections.emptyMap(); 609 } 610 else 611 { 612 this.extensions = Collections.unmodifiableMap(extensions); 613 } 614 615 final StringBuilder buffer = new StringBuilder(); 616 createDefinitionString(buffer); 617 attributeTypeString = buffer.toString(); 618 } 619 620 621 622 /** 623 * Constructs a string representation of this attribute type definition in the 624 * provided buffer. 625 * 626 * @param buffer The buffer in which to construct a string representation of 627 * this attribute type definition. 628 */ 629 private void createDefinitionString(final StringBuilder buffer) 630 { 631 buffer.append("( "); 632 buffer.append(oid); 633 634 if (names.length == 1) 635 { 636 buffer.append(" NAME '"); 637 buffer.append(names[0]); 638 buffer.append('\''); 639 } 640 else if (names.length > 1) 641 { 642 buffer.append(" NAME ("); 643 for (final String name : names) 644 { 645 buffer.append(" '"); 646 buffer.append(name); 647 buffer.append('\''); 648 } 649 buffer.append(" )"); 650 } 651 652 if (description != null) 653 { 654 buffer.append(" DESC '"); 655 encodeValue(description, buffer); 656 buffer.append('\''); 657 } 658 659 if (isObsolete) 660 { 661 buffer.append(" OBSOLETE"); 662 } 663 664 if (superiorType != null) 665 { 666 buffer.append(" SUP "); 667 buffer.append(superiorType); 668 } 669 670 if (equalityMatchingRule != null) 671 { 672 buffer.append(" EQUALITY "); 673 buffer.append(equalityMatchingRule); 674 } 675 676 if (orderingMatchingRule != null) 677 { 678 buffer.append(" ORDERING "); 679 buffer.append(orderingMatchingRule); 680 } 681 682 if (substringMatchingRule != null) 683 { 684 buffer.append(" SUBSTR "); 685 buffer.append(substringMatchingRule); 686 } 687 688 if (syntaxOID != null) 689 { 690 buffer.append(" SYNTAX "); 691 buffer.append(syntaxOID); 692 } 693 694 if (isSingleValued) 695 { 696 buffer.append(" SINGLE-VALUE"); 697 } 698 699 if (isCollective) 700 { 701 buffer.append(" COLLECTIVE"); 702 } 703 704 if (isNoUserModification) 705 { 706 buffer.append(" NO-USER-MODIFICATION"); 707 } 708 709 buffer.append(" USAGE "); 710 buffer.append(usage.getName()); 711 712 for (final Map.Entry<String,String[]> e : extensions.entrySet()) 713 { 714 final String name = e.getKey(); 715 final String[] values = e.getValue(); 716 if (values.length == 1) 717 { 718 buffer.append(' '); 719 buffer.append(name); 720 buffer.append(" '"); 721 encodeValue(values[0], buffer); 722 buffer.append('\''); 723 } 724 else 725 { 726 buffer.append(' '); 727 buffer.append(name); 728 buffer.append(" ("); 729 for (final String value : values) 730 { 731 buffer.append(" '"); 732 encodeValue(value, buffer); 733 buffer.append('\''); 734 } 735 buffer.append(" )"); 736 } 737 } 738 739 buffer.append(" )"); 740 } 741 742 743 744 /** 745 * Retrieves the OID for this attribute type. 746 * 747 * @return The OID for this attribute type. 748 */ 749 public String getOID() 750 { 751 return oid; 752 } 753 754 755 756 /** 757 * Retrieves the set of names for this attribute type. 758 * 759 * @return The set of names for this attribute type, or an empty array if it 760 * does not have any names. 761 */ 762 public String[] getNames() 763 { 764 return names; 765 } 766 767 768 769 /** 770 * Retrieves the primary name that can be used to reference this attribute 771 * type. If one or more names are defined, then the first name will be used. 772 * Otherwise, the OID will be returned. 773 * 774 * @return The primary name that can be used to reference this attribute 775 * type. 776 */ 777 public String getNameOrOID() 778 { 779 if (names.length == 0) 780 { 781 return oid; 782 } 783 else 784 { 785 return names[0]; 786 } 787 } 788 789 790 791 /** 792 * Indicates whether the provided string matches the OID or any of the names 793 * for this attribute type. 794 * 795 * @param s The string for which to make the determination. It must not be 796 * {@code null}. 797 * 798 * @return {@code true} if the provided string matches the OID or any of the 799 * names for this attribute type, or {@code false} if not. 800 */ 801 public boolean hasNameOrOID(final String s) 802 { 803 for (final String name : names) 804 { 805 if (s.equalsIgnoreCase(name)) 806 { 807 return true; 808 } 809 } 810 811 return s.equalsIgnoreCase(oid); 812 } 813 814 815 816 /** 817 * Retrieves the description for this attribute type, if available. 818 * 819 * @return The description for this attribute type, or {@code null} if there 820 * is no description defined. 821 */ 822 public String getDescription() 823 { 824 return description; 825 } 826 827 828 829 /** 830 * Indicates whether this attribute type is declared obsolete. 831 * 832 * @return {@code true} if this attribute type is declared obsolete, or 833 * {@code false} if it is not. 834 */ 835 public boolean isObsolete() 836 { 837 return isObsolete; 838 } 839 840 841 842 /** 843 * Retrieves the name or OID of the superior type for this attribute type, if 844 * available. 845 * 846 * @return The name or OID of the superior type for this attribute type, or 847 * {@code null} if no superior type is defined. 848 */ 849 public String getSuperiorType() 850 { 851 return superiorType; 852 } 853 854 855 856 /** 857 * Retrieves the superior attribute type definition for this attribute type, 858 * if available. 859 * 860 * @param schema The schema to use to get the superior attribute type. 861 * 862 * @return The superior attribute type definition for this attribute type, or 863 * {@code null} if no superior type is defined, or if the superior 864 * type is not included in the provided schema. 865 */ 866 public AttributeTypeDefinition getSuperiorType(final Schema schema) 867 { 868 if (superiorType != null) 869 { 870 return schema.getAttributeType(superiorType); 871 } 872 873 return null; 874 } 875 876 877 878 /** 879 * Retrieves the name or OID of the equality matching rule for this attribute 880 * type, if available. 881 * 882 * @return The name or OID of the equality matching rule for this attribute 883 * type, or {@code null} if no equality matching rule is defined or a 884 * default rule will be inherited. 885 */ 886 public String getEqualityMatchingRule() 887 { 888 return equalityMatchingRule; 889 } 890 891 892 893 /** 894 * Retrieves the name or OID of the equality matching rule for this attribute 895 * type, examining superior attribute types if necessary. 896 * 897 * @param schema The schema to use to get the superior attribute type. 898 * 899 * @return The name or OID of the equality matching rule for this attribute 900 * type, or {@code null} if no equality matching rule is defined. 901 */ 902 public String getEqualityMatchingRule(final Schema schema) 903 { 904 if (equalityMatchingRule == null) 905 { 906 final AttributeTypeDefinition sup = getSuperiorType(schema); 907 if (sup != null) 908 { 909 return sup.getEqualityMatchingRule(schema); 910 } 911 } 912 913 return equalityMatchingRule; 914 } 915 916 917 918 /** 919 * Retrieves the name or OID of the ordering matching rule for this attribute 920 * type, if available. 921 * 922 * @return The name or OID of the ordering matching rule for this attribute 923 * type, or {@code null} if no ordering matching rule is defined or a 924 * default rule will be inherited. 925 */ 926 public String getOrderingMatchingRule() 927 { 928 return orderingMatchingRule; 929 } 930 931 932 933 /** 934 * Retrieves the name or OID of the ordering matching rule for this attribute 935 * type, examining superior attribute types if necessary. 936 * 937 * @param schema The schema to use to get the superior attribute type. 938 * 939 * @return The name or OID of the ordering matching rule for this attribute 940 * type, or {@code null} if no ordering matching rule is defined. 941 */ 942 public String getOrderingMatchingRule(final Schema schema) 943 { 944 if (orderingMatchingRule == null) 945 { 946 final AttributeTypeDefinition sup = getSuperiorType(schema); 947 if (sup != null) 948 { 949 return sup.getOrderingMatchingRule(schema); 950 } 951 } 952 953 return orderingMatchingRule; 954 } 955 956 957 958 /** 959 * Retrieves the name or OID of the substring matching rule for this attribute 960 * type, if available. 961 * 962 * @return The name or OID of the substring matching rule for this attribute 963 * type, or {@code null} if no substring matching rule is defined or 964 * a default rule will be inherited. 965 */ 966 public String getSubstringMatchingRule() 967 { 968 return substringMatchingRule; 969 } 970 971 972 973 /** 974 * Retrieves the name or OID of the substring matching rule for this attribute 975 * type, examining superior attribute types if necessary. 976 * 977 * @param schema The schema to use to get the superior attribute type. 978 * 979 * @return The name or OID of the substring matching rule for this attribute 980 * type, or {@code null} if no substring matching rule is defined. 981 */ 982 public String getSubstringMatchingRule(final Schema schema) 983 { 984 if (substringMatchingRule == null) 985 { 986 final AttributeTypeDefinition sup = getSuperiorType(schema); 987 if (sup != null) 988 { 989 return sup.getSubstringMatchingRule(schema); 990 } 991 } 992 993 return substringMatchingRule; 994 } 995 996 997 998 /** 999 * Retrieves the OID of the syntax for this attribute type, if available. It 1000 * may optionally include a minimum upper bound in curly braces. 1001 * 1002 * @return The OID of the syntax for this attribute type, or {@code null} if 1003 * the syntax will be inherited. 1004 */ 1005 public String getSyntaxOID() 1006 { 1007 return syntaxOID; 1008 } 1009 1010 1011 1012 /** 1013 * Retrieves the OID of the syntax for this attribute type, examining superior 1014 * types if necessary. It may optionally include a minimum upper bound in 1015 * curly braces. 1016 * 1017 * @param schema The schema to use to get the superior attribute type. 1018 * 1019 * @return The OID of the syntax for this attribute type, or {@code null} if 1020 * no syntax is defined. 1021 */ 1022 public String getSyntaxOID(final Schema schema) 1023 { 1024 if (syntaxOID == null) 1025 { 1026 final AttributeTypeDefinition sup = getSuperiorType(schema); 1027 if (sup != null) 1028 { 1029 return sup.getSyntaxOID(schema); 1030 } 1031 } 1032 1033 return syntaxOID; 1034 } 1035 1036 1037 1038 /** 1039 * Retrieves the OID of the syntax for this attribute type, if available. If 1040 * the attribute type definition includes a minimum upper bound in curly 1041 * braces, it will be removed from the value that is returned. 1042 * 1043 * @return The OID of the syntax for this attribute type, or {@code null} if 1044 * the syntax will be inherited. 1045 */ 1046 public String getBaseSyntaxOID() 1047 { 1048 return getBaseSyntaxOID(syntaxOID); 1049 } 1050 1051 1052 1053 /** 1054 * Retrieves the base OID of the syntax for this attribute type, examining 1055 * superior types if necessary. If the attribute type definition includes a 1056 * minimum upper bound in curly braces, it will be removed from the value that 1057 * is returned. 1058 * 1059 * @param schema The schema to use to get the superior attribute type, if 1060 * necessary. 1061 * 1062 * @return The OID of the syntax for this attribute type, or {@code null} if 1063 * no syntax is defined. 1064 */ 1065 public String getBaseSyntaxOID(final Schema schema) 1066 { 1067 return getBaseSyntaxOID(getSyntaxOID(schema)); 1068 } 1069 1070 1071 1072 /** 1073 * Retrieves the base OID of the syntax for this attribute type, examining 1074 * superior types if necessary. If the attribute type definition includes a 1075 * minimum upper bound in curly braces, it will be removed from the value that 1076 * is returned. 1077 * 1078 * @param syntaxOID The syntax OID (optionally including the minimum upper 1079 * bound element) to examine. 1080 * 1081 * @return The OID of the syntax for this attribute type, or {@code null} if 1082 * no syntax is defined. 1083 */ 1084 public static String getBaseSyntaxOID(final String syntaxOID) 1085 { 1086 if (syntaxOID == null) 1087 { 1088 return null; 1089 } 1090 1091 final int curlyPos = syntaxOID.indexOf('{'); 1092 if (curlyPos > 0) 1093 { 1094 return syntaxOID.substring(0, curlyPos); 1095 } 1096 else 1097 { 1098 return syntaxOID; 1099 } 1100 } 1101 1102 1103 1104 /** 1105 * Retrieves the value of the minimum upper bound element of the syntax 1106 * definition for this attribute type, if defined. If a minimum upper bound 1107 * is present (as signified by an integer value in curly braces immediately 1108 * following the syntax OID without any space between them), then it should 1109 * serve as an indication to the directory server that it should be prepared 1110 * to handle values with at least that number of (possibly multi-byte) 1111 * characters. 1112 * 1113 * @return The value of the minimum upper bound element of the syntax 1114 * definition for this attribute type, or -1 if no syntax is defined 1115 * defined or if it does not have a valid minimum upper bound. 1116 */ 1117 public int getSyntaxMinimumUpperBound() 1118 { 1119 return getSyntaxMinimumUpperBound(syntaxOID); 1120 } 1121 1122 1123 1124 /** 1125 * Retrieves the value of the minimum upper bound element of the syntax 1126 * definition for this attribute type, if defined. If a minimum upper bound 1127 * is present (as signified by an integer value in curly braces immediately 1128 * following the syntax OID without any space between them), then it should 1129 * serve as an indication to the directory server that it should be prepared 1130 * to handle values with at least that number of (possibly multi-byte) 1131 * characters. 1132 * 1133 * @param schema The schema to use to get the superior attribute type, if 1134 * necessary. 1135 * 1136 * @return The value of the minimum upper bound element of the syntax 1137 * definition for this attribute type, or -1 if no syntax is defined 1138 * defined or if it does not have a valid minimum upper bound. 1139 */ 1140 public int getSyntaxMinimumUpperBound(final Schema schema) 1141 { 1142 return getSyntaxMinimumUpperBound(getSyntaxOID(schema)); 1143 } 1144 1145 1146 1147 /** 1148 * Retrieves the value of the minimum upper bound element of the syntax 1149 * definition for this attribute type, if defined. If a minimum upper bound 1150 * is present (as signified by an integer value in curly braces immediately 1151 * following the syntax OID without any space between them), then it should 1152 * serve as an indication to the directory server that it should be prepared 1153 * to handle values with at least that number of (possibly multi-byte) 1154 * characters. 1155 * 1156 * @param syntaxOID The syntax OID (optionally including the minimum upper 1157 * bound element) to examine. 1158 * 1159 * @return The value of the minimum upper bound element of the provided 1160 * syntax OID, or -1 if the provided syntax OID is {@code null} or 1161 * does not have a valid minimum upper bound. 1162 */ 1163 public static int getSyntaxMinimumUpperBound(final String syntaxOID) 1164 { 1165 if (syntaxOID == null) 1166 { 1167 return -1; 1168 } 1169 1170 final int curlyPos = syntaxOID.indexOf('{'); 1171 if ((curlyPos > 0) && syntaxOID.endsWith("}")) 1172 { 1173 try 1174 { 1175 return Integer.parseInt(syntaxOID.substring(curlyPos+1, 1176 syntaxOID.length()-1)); 1177 } 1178 catch (final Exception e) 1179 { 1180 debugException(e); 1181 return -1; 1182 } 1183 } 1184 else 1185 { 1186 return -1; 1187 } 1188 } 1189 1190 1191 1192 /** 1193 * Indicates whether this attribute type is declared single-valued, and 1194 * therefore attributes of this type will only be allowed to have at most one 1195 * value. 1196 * 1197 * @return {@code true} if this attribute type is declared single-valued, or 1198 * {@code false} if not. 1199 */ 1200 public boolean isSingleValued() 1201 { 1202 return isSingleValued; 1203 } 1204 1205 1206 1207 /** 1208 * Indicates whether this attribute type is declared collective, and therefore 1209 * values may be dynamically generated as described in RFC 3671. 1210 * 1211 * @return {@code true} if this attribute type is declared collective, or 1212 * {@code false} if not. 1213 */ 1214 public boolean isCollective() 1215 { 1216 return isCollective; 1217 } 1218 1219 1220 1221 /** 1222 * Indicates whether this attribute type is declared no-user-modification, 1223 * and therefore attributes of this type will not be allowed to be altered 1224 * by clients. 1225 * 1226 * @return {@code true} if this attribute type is declared 1227 * no-user-modification, or {@code false} if not. 1228 */ 1229 public boolean isNoUserModification() 1230 { 1231 return isNoUserModification; 1232 } 1233 1234 1235 1236 /** 1237 * Retrieves the attribute usage for this attribute type. 1238 * 1239 * @return The attribute usage for this attribute type. 1240 */ 1241 public AttributeUsage getUsage() 1242 { 1243 return usage; 1244 } 1245 1246 1247 1248 /** 1249 * Indicates whether this attribute type has an operational attribute usage. 1250 * 1251 * @return {@code true} if this attribute type has an operational attribute 1252 * usage, or {@code false} if not. 1253 */ 1254 public boolean isOperational() 1255 { 1256 return usage.isOperational(); 1257 } 1258 1259 1260 1261 /** 1262 * Retrieves the set of extensions for this attribute type. They will be 1263 * mapped from the extension name (which should start with "X-") to the set of 1264 * values for that extension. 1265 * 1266 * @return The set of extensions for this attribute type. 1267 */ 1268 public Map<String,String[]> getExtensions() 1269 { 1270 return extensions; 1271 } 1272 1273 1274 1275 /** 1276 * {@inheritDoc} 1277 */ 1278 @Override() 1279 public int hashCode() 1280 { 1281 return oid.hashCode(); 1282 } 1283 1284 1285 1286 /** 1287 * {@inheritDoc} 1288 */ 1289 @Override() 1290 public boolean equals(final Object o) 1291 { 1292 if (o == null) 1293 { 1294 return false; 1295 } 1296 1297 if (o == this) 1298 { 1299 return true; 1300 } 1301 1302 if (! (o instanceof AttributeTypeDefinition)) 1303 { 1304 return false; 1305 } 1306 1307 final AttributeTypeDefinition d = (AttributeTypeDefinition) o; 1308 return(oid.equals(d.oid) && 1309 stringsEqualIgnoreCaseOrderIndependent(names, d.names) && 1310 bothNullOrEqual(usage, d.usage) && 1311 bothNullOrEqualIgnoreCase(description, d.description) && 1312 bothNullOrEqualIgnoreCase(equalityMatchingRule, 1313 d.equalityMatchingRule) && 1314 bothNullOrEqualIgnoreCase(orderingMatchingRule, 1315 d.orderingMatchingRule) && 1316 bothNullOrEqualIgnoreCase(substringMatchingRule, 1317 d.substringMatchingRule) && 1318 bothNullOrEqualIgnoreCase(superiorType, d.superiorType) && 1319 bothNullOrEqualIgnoreCase(syntaxOID, d.syntaxOID) && 1320 (isCollective == d.isCollective) && 1321 (isNoUserModification == d.isNoUserModification) && 1322 (isObsolete == d.isObsolete) && 1323 (isSingleValued == d.isSingleValued) && 1324 extensionsEqual(extensions, d.extensions)); 1325 } 1326 1327 1328 1329 /** 1330 * Retrieves a string representation of this attribute type definition, in the 1331 * format described in RFC 4512 section 4.1.2. 1332 * 1333 * @return A string representation of this attribute type definition. 1334 */ 1335 @Override() 1336 public String toString() 1337 { 1338 return attributeTypeString; 1339 } 1340}