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