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