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