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