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