001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2009 Sun Microsystems, Inc. 015 * Portions copyright 2011-2016 ForgeRock AS. 016 */ 017package org.forgerock.opendj.ldap.schema; 018 019import static java.util.Arrays.*; 020 021import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; 022import static org.forgerock.opendj.ldap.schema.SchemaOptions.ALLOW_ATTRIBUTE_TYPES_WITH_NO_SUP_OR_SYNTAX; 023import static org.forgerock.opendj.ldap.schema.SchemaUtils.*; 024 025import static com.forgerock.opendj.ldap.CoreMessages.*; 026import static com.forgerock.opendj.util.StaticUtils.*; 027 028import java.util.Collection; 029import java.util.Collections; 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Map; 034 035import org.forgerock.i18n.LocalizableMessage; 036import org.forgerock.util.Reject; 037 038import com.forgerock.opendj.util.StaticUtils; 039 040/** 041 * This class defines a data structure for storing and interacting with an 042 * attribute type, which contains information about the format of an attribute 043 * and the syntax and matching rules that should be used when interacting with 044 * it. 045 * <p> 046 * Where ordered sets of names, or extra properties are provided, the ordering 047 * will be preserved when the associated fields are accessed via their getters 048 * or via the {@link #toString()} methods. 049 */ 050public final class AttributeType extends AbstractSchemaElement implements Comparable<AttributeType> { 051 052 /** A fluent API for incrementally constructing attribute type. */ 053 public static final class Builder extends SchemaElementBuilder<Builder> { 054 private String oid; 055 private final List<String> names = new LinkedList<>(); 056 private AttributeUsage attributeUsage; 057 private boolean isCollective; 058 private boolean isNoUserModification; 059 private boolean isObsolete; 060 private boolean isSingleValue; 061 private String approximateMatchingRuleOID; 062 private String equalityMatchingRuleOID; 063 private String orderingMatchingRuleOID; 064 private String substringMatchingRuleOID; 065 private String superiorTypeOID; 066 private String syntaxOID; 067 068 Builder(final AttributeType at, final SchemaBuilder builder) { 069 super(builder, at); 070 this.oid = at.oid; 071 this.attributeUsage = at.attributeUsage; 072 this.isCollective = at.isCollective; 073 this.isNoUserModification = at.isNoUserModification; 074 this.isObsolete = at.isObsolete; 075 this.isSingleValue = at.isSingleValue; 076 this.names.addAll(at.names); 077 this.approximateMatchingRuleOID = at.approximateMatchingRuleOID; 078 this.equalityMatchingRuleOID = at.equalityMatchingRuleOID; 079 this.orderingMatchingRuleOID = at.orderingMatchingRuleOID; 080 this.substringMatchingRuleOID = at.substringMatchingRuleOID; 081 this.superiorTypeOID = at.superiorTypeOID; 082 this.syntaxOID = at.syntaxOID; 083 } 084 085 Builder(final String oid, final SchemaBuilder builder) { 086 super(builder); 087 this.oid = oid; 088 } 089 090 /** 091 * Adds this attribute type to the schema, throwing a 092 * {@code ConflictingSchemaElementException} if there is an existing 093 * attribute type with the same numeric OID. 094 * 095 * @return The parent schema builder. 096 * @throws ConflictingSchemaElementException 097 * If there is an existing attribute type with the same 098 * numeric OID. 099 */ 100 public SchemaBuilder addToSchema() { 101 return addToSchema(false); 102 } 103 104 /** 105 * Adds this attribute type to the schema overwriting any existing 106 * attribute type with the same numeric OID. 107 * 108 * @return The parent schema builder. 109 */ 110 public SchemaBuilder addToSchemaOverwrite() { 111 return addToSchema(true); 112 } 113 114 SchemaBuilder addToSchema(final boolean overwrite) { 115 return getSchemaBuilder().addAttributeType(new AttributeType(this), overwrite); 116 } 117 118 /** 119 * Sets the matching rule that should be used for approximate matching 120 * with this attribute type. 121 * 122 * @param approximateMatchingRuleOID 123 * The matching rule OID. 124 * @return This builder. 125 */ 126 public Builder approximateMatchingRule(String approximateMatchingRuleOID) { 127 this.approximateMatchingRuleOID = approximateMatchingRuleOID; 128 return this; 129 } 130 131 /** 132 * Specifies whether this attribute type is "collective". 133 * 134 * @param isCollective 135 * {@code true} if this attribute type is "collective". 136 * @return This builder. 137 */ 138 public Builder collective(boolean isCollective) { 139 this.isCollective = isCollective; 140 return this; 141 } 142 143 @Override 144 public Builder description(String description) { 145 return description0(description); 146 } 147 148 /** 149 * Sets the matching rule that should be used for equality matching with 150 * this attribute type. 151 * 152 * @param equalityMatchingRuleOID 153 * The matching rule OID. 154 * @return This builder. 155 */ 156 public Builder equalityMatchingRule(String equalityMatchingRuleOID) { 157 this.equalityMatchingRuleOID = equalityMatchingRuleOID; 158 return this; 159 } 160 161 @Override 162 public Builder extraProperties(Map<String, List<String>> extraProperties) { 163 return extraProperties0(extraProperties); 164 } 165 166 @Override 167 public Builder extraProperties(String extensionName, String... extensionValues) { 168 return extraProperties0(extensionName, extensionValues); 169 } 170 171 @Override 172 Builder getThis() { 173 return this; 174 } 175 176 /** 177 * Adds the provided user friendly names. 178 * 179 * @param names 180 * The user friendly names. 181 * @return This builder. 182 */ 183 public Builder names(final Collection<String> names) { 184 this.names.addAll(names); 185 return this; 186 } 187 188 /** 189 * Adds the provided user friendly names. 190 * 191 * @param names 192 * The user friendly names. 193 * @return This builder. 194 */ 195 public Builder names(final String... names) { 196 return names(asList(names)); 197 } 198 199 /** 200 * Specifies whether this attribute type is "no-user-modification". 201 * 202 * @param isNoUserModification 203 * {@code true} if this attribute type is 204 * "no-user-modification" 205 * @return This builder. 206 */ 207 public Builder noUserModification(boolean isNoUserModification) { 208 this.isNoUserModification = isNoUserModification; 209 return this; 210 } 211 212 /** 213 * Specifies whether this schema element is obsolete. 214 * 215 * @param isObsolete 216 * {@code true} if this schema element is obsolete (default 217 * is {@code false}). 218 * @return This builder. 219 */ 220 public Builder obsolete(final boolean isObsolete) { 221 this.isObsolete = isObsolete; 222 return this; 223 } 224 225 /** 226 * Sets the numeric OID which uniquely identifies this attribute type. 227 * 228 * @param oid 229 * The numeric OID. 230 * @return This builder. 231 */ 232 public Builder oid(final String oid) { 233 this.oid = oid; 234 return this; 235 } 236 237 /** 238 * Sets the matching rule that should be used for ordering with this 239 * attribute type. 240 * 241 * @param orderingMatchingRuleOID 242 * The matching rule OID. 243 * @return This Builder. 244 */ 245 public Builder orderingMatchingRule(String orderingMatchingRuleOID) { 246 this.orderingMatchingRuleOID = orderingMatchingRuleOID; 247 return this; 248 } 249 250 @Override 251 public Builder removeAllExtraProperties() { 252 return removeAllExtraProperties0(); 253 } 254 255 /** 256 * Removes all user defined names. 257 * 258 * @return This builder. 259 */ 260 public Builder removeAllNames() { 261 this.names.clear(); 262 return this; 263 } 264 265 @Override 266 public Builder removeExtraProperty(String extensionName, String... extensionValues) { 267 return removeExtraProperty0(extensionName, extensionValues); 268 } 269 270 /** 271 * Removes the provided user defined name. 272 * 273 * @param name 274 * The user defined name to be removed. 275 * @return This builder. 276 */ 277 public Builder removeName(String name) { 278 this.names.remove(name); 279 return this; 280 } 281 282 /** 283 * Specifies whether this attribute type is declared "single-value". 284 * 285 * @param isSingleValue 286 * {@code true} if this attribute type is declared 287 * "single-value". 288 * @return This builder. 289 */ 290 public Builder singleValue(boolean isSingleValue) { 291 this.isSingleValue = isSingleValue; 292 return this; 293 } 294 295 /** 296 * Sets the matching rule that should be used for substring matching 297 * with this attribute type. 298 * 299 * @param substringMatchingRuleOID 300 * The matching rule OID. 301 * @return This builder. 302 */ 303 public Builder substringMatchingRule(String substringMatchingRuleOID) { 304 this.substringMatchingRuleOID = substringMatchingRuleOID; 305 return this; 306 } 307 308 /** 309 * Sets the superior type for this attribute type. 310 * 311 * @param superiorTypeOID 312 * The superior type OID. 313 * @return This builder. 314 */ 315 public Builder superiorType(String superiorTypeOID) { 316 this.superiorTypeOID = superiorTypeOID; 317 return this; 318 } 319 320 /** 321 * Sets the syntax for this attribute type. 322 * 323 * @param syntaxOID 324 * The syntax OID. 325 * @return This builder. 326 */ 327 public Builder syntax(String syntaxOID) { 328 this.syntaxOID = syntaxOID; 329 return this; 330 } 331 332 /** 333 * Sets the usage indicator for this attribute type. 334 * 335 * @param attributeUsage 336 * The attribute usage. 337 * @return This builder. 338 */ 339 public Builder usage(AttributeUsage attributeUsage) { 340 this.attributeUsage = attributeUsage; 341 return this; 342 } 343 } 344 345 /** The approximate matching rule for this attribute type. */ 346 private final String approximateMatchingRuleOID; 347 348 /** The attribute usage for this attribute type. */ 349 private final AttributeUsage attributeUsage; 350 351 /** The equality matching rule for this attribute type. */ 352 private final String equalityMatchingRuleOID; 353 354 /** Indicates whether this attribute type is declared "collective". */ 355 private final boolean isCollective; 356 357 /** Indicates whether this attribute type is declared "no-user-modification". */ 358 private final boolean isNoUserModification; 359 360 /** Indicates whether this definition is declared "obsolete". */ 361 private final boolean isObsolete; 362 363 /** Indicates whether this definition is a temporary place-holder. */ 364 private final boolean isPlaceHolder; 365 366 /** Indicates whether this attribute type is declared "single-value". */ 367 private final boolean isSingleValue; 368 369 /** The set of user defined names for this definition. */ 370 private final List<String> names; 371 372 /** The OID that may be used to reference this definition. */ 373 private final String oid; 374 375 /** The ordering matching rule for this attribute type. */ 376 private final String orderingMatchingRuleOID; 377 378 /** The substring matching rule for this attribute type. */ 379 private final String substringMatchingRuleOID; 380 381 /** The superior attribute type from which this attribute type inherits. */ 382 private final String superiorTypeOID; 383 384 /** The syntax for this attribute type. */ 385 private final String syntaxOID; 386 387 /** True if this type has OID 2.5.4.0. */ 388 private final boolean isObjectClassType; 389 390 /** The normalized name of this attribute type. */ 391 private final String normalizedName; 392 393 /** The superior attribute type from which this attribute type inherits. */ 394 private AttributeType superiorType; 395 396 /** The equality matching rule for this attribute type. */ 397 private MatchingRule equalityMatchingRule; 398 399 /** The ordering matching rule for this attribute type. */ 400 private MatchingRule orderingMatchingRule; 401 402 /** The substring matching rule for this attribute type. */ 403 private MatchingRule substringMatchingRule; 404 405 /** The approximate matching rule for this attribute type. */ 406 private MatchingRule approximateMatchingRule; 407 408 /** The syntax for this attribute type. */ 409 private Syntax syntax; 410 411 /** Indicates whether validation has been performed. */ 412 private boolean needsValidating = true; 413 414 /** The indicates whether validation failed. */ 415 private boolean isValid; 416 417 private AttributeType(Builder builder) { 418 super(builder); 419 Reject.ifTrue(builder.oid == null || builder.oid.isEmpty(), "An OID must be specified."); 420 421 if (builder.superiorTypeOID == null && builder.syntaxOID == null 422 && !builder.getSchemaBuilder().getOptions().get(ALLOW_ATTRIBUTE_TYPES_WITH_NO_SUP_OR_SYNTAX)) { 423 throw new IllegalArgumentException("Superior type and/or Syntax must not be null"); 424 } 425 426 oid = builder.oid; 427 names = unmodifiableCopyOfList(builder.names); 428 attributeUsage = builder.attributeUsage; 429 isCollective = builder.isCollective; 430 isNoUserModification = builder.isNoUserModification; 431 isObjectClassType = "2.5.4.0".equals(oid); 432 isObsolete = builder.isObsolete; 433 isSingleValue = builder.isSingleValue; 434 approximateMatchingRuleOID = builder.approximateMatchingRuleOID; 435 equalityMatchingRuleOID = builder.equalityMatchingRuleOID; 436 orderingMatchingRuleOID = builder.orderingMatchingRuleOID; 437 substringMatchingRuleOID = builder.substringMatchingRuleOID; 438 superiorTypeOID = builder.superiorTypeOID; 439 syntaxOID = builder.syntaxOID; 440 isPlaceHolder = false; 441 normalizedName = toLowerCase(getNameOrOID()); 442 } 443 444 /** 445 * Creates a new place-holder attribute type having the specified name, 446 * default syntax, and default matching rule. The OID of the place-holder 447 * attribute will be the normalized attribute type name followed by the 448 * suffix "-oid". 449 * 450 * @param name 451 * The name of the place-holder attribute type. 452 * @param syntax 453 * The syntax of the place-holder attribute type. 454 * @param equalityMatchingRule 455 * The equality matching rule of the place-holder attribute type. 456 */ 457 static AttributeType newPlaceHolder(final String name, final Syntax syntax, 458 final MatchingRule equalityMatchingRule) { 459 return new AttributeType(name, syntax, equalityMatchingRule); 460 } 461 462 /** 463 * Creates a new place-holder attribute type having the specified name, default syntax, and default matching rule. 464 */ 465 private AttributeType(final String name, final Syntax syntax, final MatchingRule equalityMatchingRule) { 466 final StringBuilder builder = new StringBuilder(name.length() + 4); 467 StaticUtils.toLowerCase(name, builder); 468 builder.append("-oid"); 469 470 this.oid = builder.toString(); 471 this.names = Collections.singletonList(name); 472 this.isObsolete = false; 473 this.superiorTypeOID = null; 474 this.superiorType = null; 475 this.equalityMatchingRule = equalityMatchingRule; 476 this.equalityMatchingRuleOID = equalityMatchingRule.getOID(); 477 this.orderingMatchingRuleOID = null; 478 this.substringMatchingRuleOID = null; 479 this.approximateMatchingRuleOID = null; 480 this.syntax = syntax; 481 this.syntaxOID = syntax.getOID(); 482 this.isSingleValue = false; 483 this.isCollective = false; 484 this.isNoUserModification = false; 485 this.attributeUsage = null; 486 this.isObjectClassType = false; 487 this.isPlaceHolder = true; 488 this.normalizedName = StaticUtils.toLowerCase(getNameOrOID()); 489 } 490 491 /** 492 * Compares this attribute type to the provided attribute type. The 493 * sort-order is defined as follows: 494 * <ul> 495 * <li>The {@code objectClass} attribute is less than all other attribute 496 * types. 497 * <li>User attributes are less than operational attributes. 498 * <li>Lexicographic comparison of the primary name and then, if equal, the 499 * OID. 500 * </ul> 501 * 502 * @param type 503 * The attribute type to be compared. 504 * @return A negative integer, zero, or a positive integer as this attribute 505 * type is less than, equal to, or greater than the specified 506 * attribute type. 507 * @throws NullPointerException 508 * If {@code name} was {@code null}. 509 */ 510 @Override 511 public int compareTo(final AttributeType type) { 512 if (isObjectClassType) { 513 return type.isObjectClassType ? 0 : -1; 514 } else if (type.isObjectClassType) { 515 return 1; 516 } else { 517 final boolean isOperational = getUsage().isOperational(); 518 final boolean typeIsOperational = type.getUsage().isOperational(); 519 if (isOperational == typeIsOperational) { 520 final int tmp = normalizedName.compareTo(type.normalizedName); 521 if (tmp == 0) { 522 return oid.compareTo(type.oid); 523 } else { 524 return tmp; 525 } 526 } else { 527 return isOperational ? 1 : -1; 528 } 529 } 530 } 531 532 /** 533 * Returns {@code true} if the provided object is an attribute type having 534 * the same numeric OID as this attribute type. 535 * 536 * @param o 537 * The object to be compared. 538 * @return {@code true} if the provided object is an attribute type having 539 * the same numeric OID as this attribute type. 540 */ 541 @Override 542 public boolean equals(final Object o) { 543 if (this == o) { 544 return true; 545 } else if (o instanceof AttributeType) { 546 final AttributeType other = (AttributeType) o; 547 return oid.equals(other.oid); 548 } else { 549 return false; 550 } 551 } 552 553 /** 554 * Returns the matching rule that should be used for approximate matching 555 * with this attribute type. 556 * 557 * @return The matching rule that should be used for approximate matching 558 * with this attribute type. 559 */ 560 public MatchingRule getApproximateMatchingRule() { 561 return approximateMatchingRule; 562 } 563 564 /** 565 * Returns the matching rule that should be used for equality matching with 566 * this attribute type. 567 * 568 * @return The matching rule that should be used for equality matching with 569 * this attribute type. 570 */ 571 public MatchingRule getEqualityMatchingRule() { 572 return equalityMatchingRule; 573 } 574 575 /** 576 * Returns the name or OID for this schema definition. If it has one or more 577 * names, then the primary name will be returned. If it does not have any 578 * names, then the OID will be returned. 579 * 580 * @return The name or OID for this schema definition. 581 */ 582 public String getNameOrOID() { 583 if (names.isEmpty()) { 584 return oid; 585 } 586 return names.get(0); 587 } 588 589 /** 590 * Returns an unmodifiable list containing the user-defined names that may 591 * be used to reference this schema definition. 592 * 593 * @return Returns an unmodifiable list containing the user-defined names 594 * that may be used to reference this schema definition. 595 */ 596 public List<String> getNames() { 597 return names; 598 } 599 600 /** 601 * Returns the OID for this schema definition. 602 * 603 * @return The OID for this schema definition. 604 */ 605 public String getOID() { 606 return oid; 607 } 608 609 /** 610 * Returns the matching rule that should be used for ordering with this 611 * attribute type. 612 * 613 * @return The matching rule that should be used for ordering with this 614 * attribute type. 615 */ 616 public MatchingRule getOrderingMatchingRule() { 617 return orderingMatchingRule; 618 } 619 620 /** 621 * Returns the matching rule that should be used for substring matching with 622 * this attribute type. 623 * 624 * @return The matching rule that should be used for substring matching with 625 * this attribute type. 626 */ 627 public MatchingRule getSubstringMatchingRule() { 628 return substringMatchingRule; 629 } 630 631 /** 632 * Returns the superior type for this attribute type. 633 * 634 * @return The superior type for this attribute type, or <CODE>null</CODE> 635 * if it does not have one. 636 */ 637 public AttributeType getSuperiorType() { 638 return superiorType; 639 } 640 641 /** 642 * Returns the syntax for this attribute type. 643 * 644 * @return The syntax for this attribute type. 645 */ 646 public Syntax getSyntax() { 647 return syntax; 648 } 649 650 /** 651 * Returns the usage indicator for this attribute type. 652 * 653 * @return The usage indicator for this attribute type. 654 */ 655 public AttributeUsage getUsage() { 656 return attributeUsage != null ? attributeUsage : AttributeUsage.USER_APPLICATIONS; 657 } 658 659 /** 660 * Returns the hash code for this attribute type. It will be calculated as 661 * the hash code of the numeric OID. 662 * 663 * @return The hash code for this attribute type. 664 */ 665 @Override 666 public int hashCode() { 667 return oid.hashCode(); 668 } 669 670 /** 671 * Indicates whether this schema definition has the specified name. 672 * 673 * @param name 674 * The name for which to make the determination. 675 * @return {@code true} if the specified name is assigned to this schema 676 * definition, or {@code false} if not. 677 */ 678 public boolean hasName(final String name) { 679 for (final String n : names) { 680 if (n.equalsIgnoreCase(name)) { 681 return true; 682 } 683 } 684 return false; 685 } 686 687 /** 688 * Indicates whether this schema definition has the specified name or OID. 689 * 690 * @param value 691 * The value for which to make the determination. 692 * @return {@code true} if the provided value matches the OID or one of the 693 * names assigned to this schema definition, or {@code false} if 694 * not. 695 */ 696 public boolean hasNameOrOID(final String value) { 697 return hasName(value) || getOID().equals(value); 698 } 699 700 /** 701 * Indicates whether this attribute type is declared "collective". 702 * 703 * @return {@code true} if this attribute type is declared "collective", or 704 * {@code false} if not. 705 */ 706 public boolean isCollective() { 707 return isCollective; 708 } 709 710 /** 711 * Indicates whether this attribute type is declared "no-user-modification". 712 * 713 * @return {@code true} if this attribute type is declared 714 * "no-user-modification", or {@code false} if not. 715 */ 716 public boolean isNoUserModification() { 717 return isNoUserModification; 718 } 719 720 /** 721 * Indicates whether this attribute type is the {@code objectClass} 722 * attribute type having the OID 2.5.4.0. 723 * 724 * @return {@code true} if this attribute type is the {@code objectClass} 725 * attribute type, or {@code false} if not. 726 */ 727 public boolean isObjectClass() { 728 return isObjectClassType; 729 } 730 731 /** 732 * Indicates whether this schema definition is declared "obsolete". 733 * 734 * @return {@code true} if this schema definition is declared "obsolete", or 735 * {@code false} if not. 736 */ 737 public boolean isObsolete() { 738 return isObsolete; 739 } 740 741 /** 742 * Indicates whether this is an operational attribute. An operational 743 * attribute is one with a usage of "directoryOperation", 744 * "distributedOperation", or "dSAOperation" (i.e., only userApplications is 745 * not operational). 746 * 747 * @return {@code true} if this is an operational attribute, or 748 * {@code false} if not. 749 */ 750 public boolean isOperational() { 751 return getUsage().isOperational(); 752 } 753 754 /** 755 * Indicates whether this attribute type is a temporary place-holder 756 * allocated dynamically by a non-strict schema when no registered attribute 757 * type was found. 758 * <p> 759 * Place holder attribute types have an OID which is the normalized 760 * attribute name with the string {@code -oid} appended. In addition, they 761 * will use the directory string syntax and case ignore matching rule. 762 * 763 * @return {@code true} if this is a temporary place-holder attribute type 764 * allocated dynamically by a non-strict schema when no registered 765 * attribute type was found. 766 * @see Schema#getAttributeType(String) 767 */ 768 public boolean isPlaceHolder() { 769 return isPlaceHolder; 770 } 771 772 /** 773 * Indicates whether this attribute type is declared "single-value". 774 * 775 * @return {@code true} if this attribute type is declared "single-value", 776 * or {@code false} if not. 777 */ 778 public boolean isSingleValue() { 779 return isSingleValue; 780 } 781 782 /** 783 * Indicates whether this attribute type is a sub-type of the 784 * provided attribute type. 785 * 786 * @param type 787 * The attribute type for which to make the determination. 788 * @return {@code true} if this attribute type is a sub-type of the provided 789 * attribute type, or {@code false} if not. 790 * @throws NullPointerException 791 * If {@code type} was {@code null}. 792 */ 793 public boolean isSubTypeOf(final AttributeType type) { 794 AttributeType tmp = this; 795 do { 796 if (tmp.matches(type)) { 797 return true; 798 } 799 tmp = tmp.getSuperiorType(); 800 } while (tmp != null); 801 return false; 802 } 803 804 /** 805 * Indicates whether this attribute type is a super-type of the 806 * provided attribute type. 807 * 808 * @param type 809 * The attribute type for which to make the determination. 810 * @return {@code true} if this attribute type is a super-type of the 811 * provided attribute type, or {@code false} if not. 812 * @throws NullPointerException 813 * If {@code type} was {@code null}. 814 */ 815 public boolean isSuperTypeOf(final AttributeType type) { 816 return type.isSubTypeOf(this); 817 } 818 819 /** 820 * Implements a place-holder tolerant version of {@link #equals}. This 821 * method returns {@code true} in the following cases: 822 * <ul> 823 * <li>this attribute type is equal to the provided attribute type as 824 * specified by {@link #equals} 825 * <li>this attribute type is a place-holder and the provided attribute type 826 * has a name which matches the name of this attribute type 827 * <li>the provided attribute type is a place-holder and this attribute type 828 * has a name which matches the name of the provided attribute type. 829 * </ul> 830 * 831 * @param type 832 * The attribute type for which to make the determination. 833 * @return {@code true} if the provided attribute type matches this 834 * attribute type. 835 */ 836 public boolean matches(final AttributeType type) { 837 if (this == type) { 838 return true; 839 } else if (oid.equals(type.oid)) { 840 return true; 841 } else if (isPlaceHolder != type.isPlaceHolder) { 842 return isPlaceHolder ? type.hasName(normalizedName) : hasName(type.normalizedName); 843 } else { 844 return false; 845 } 846 } 847 848 @Override 849 void toStringContent(final StringBuilder buffer) { 850 buffer.append(oid); 851 852 if (!names.isEmpty()) { 853 final Iterator<String> iterator = names.iterator(); 854 855 final String firstName = iterator.next(); 856 if (iterator.hasNext()) { 857 buffer.append(" NAME ( '"); 858 buffer.append(firstName); 859 860 while (iterator.hasNext()) { 861 buffer.append("' '"); 862 buffer.append(iterator.next()); 863 } 864 865 buffer.append("' )"); 866 } else { 867 buffer.append(" NAME '"); 868 buffer.append(firstName); 869 buffer.append("'"); 870 } 871 } 872 873 appendDescription(buffer); 874 875 if (isObsolete) { 876 buffer.append(" OBSOLETE"); 877 } 878 879 if (superiorTypeOID != null) { 880 buffer.append(" SUP "); 881 buffer.append(superiorTypeOID); 882 } 883 884 if (equalityMatchingRuleOID != null) { 885 buffer.append(" EQUALITY "); 886 buffer.append(equalityMatchingRuleOID); 887 } 888 889 if (orderingMatchingRuleOID != null) { 890 buffer.append(" ORDERING "); 891 buffer.append(orderingMatchingRuleOID); 892 } 893 894 if (substringMatchingRuleOID != null) { 895 buffer.append(" SUBSTR "); 896 buffer.append(substringMatchingRuleOID); 897 } 898 899 if (syntaxOID != null) { 900 buffer.append(" SYNTAX "); 901 buffer.append(syntaxOID); 902 } 903 904 if (isSingleValue()) { 905 buffer.append(" SINGLE-VALUE"); 906 } 907 908 if (isCollective()) { 909 buffer.append(" COLLECTIVE"); 910 } 911 912 if (isNoUserModification()) { 913 buffer.append(" NO-USER-MODIFICATION"); 914 } 915 916 if (attributeUsage != null) { 917 buffer.append(" USAGE "); 918 buffer.append(attributeUsage); 919 } 920 921 if (approximateMatchingRuleOID != null) { 922 buffer.append(" "); 923 buffer.append(SCHEMA_PROPERTY_APPROX_RULE); 924 buffer.append(" '"); 925 buffer.append(approximateMatchingRuleOID); 926 buffer.append("'"); 927 } 928 } 929 930 boolean validate(final Schema schema, final List<AttributeType> invalidSchemaElements, 931 final List<LocalizableMessage> warnings) { 932 // Avoid validating this schema element more than once. This may occur 933 // if multiple attributes specify the same superior. 934 if (!needsValidating) { 935 return isValid; 936 } 937 938 // Prevent re-validation. 939 needsValidating = false; 940 941 if (superiorTypeOID != null) { 942 try { 943 superiorType = schema.getAttributeType(superiorTypeOID); 944 } catch (final UnknownSchemaElementException e) { 945 final LocalizableMessage message = 946 WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUPERIOR_TYPE1.get(getNameOrOID(), 947 superiorTypeOID); 948 failValidation(invalidSchemaElements, warnings, message); 949 return false; 950 } 951 952 // First ensure that the superior has been validated and fail if it 953 // is invalid. 954 if (!superiorType.validate(schema, invalidSchemaElements, warnings)) { 955 final LocalizableMessage message = 956 WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_SUPERIOR_TYPE.get(getNameOrOID(), 957 superiorTypeOID); 958 failValidation(invalidSchemaElements, warnings, message); 959 return false; 960 } 961 962 // If there is a superior type, then it must have the same usage 963 // as the subordinate type. Also, if the superior type is 964 // collective, then so must the subordinate type be collective. 965 if (superiorType.getUsage() != getUsage()) { 966 final LocalizableMessage message = 967 WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_SUPERIOR_USAGE.get(getNameOrOID(), 968 getUsage().toString(), superiorType.getNameOrOID()); 969 failValidation(invalidSchemaElements, warnings, message); 970 return false; 971 } 972 973 if (superiorType.isCollective() != isCollective() && !isCollective()) { 974 LocalizableMessage message = WARN_ATTR_SYNTAX_ATTRTYPE_NONCOLLECTIVE_FROM_COLLECTIVE.get( 975 getNameOrOID(), superiorType.getNameOrOID()); 976 failValidation(invalidSchemaElements, warnings, message); 977 return false; 978 } 979 } 980 981 if (syntaxOID != null) { 982 if (!schema.hasSyntax(syntaxOID)) { 983 // Try substituting a syntax from the core schema. This will 984 // never fail since the core schema is non-strict and will 985 // substitute the syntax if required. 986 syntax = Schema.getCoreSchema().getSyntax(syntaxOID); 987 final LocalizableMessage message = 988 WARN_ATTR_TYPE_NOT_DEFINED1.get(getNameOrOID(), syntaxOID, syntax.getOID()); 989 warnings.add(message); 990 } else { 991 syntax = schema.getSyntax(syntaxOID); 992 } 993 } else if (getSuperiorType() != null && getSuperiorType().getSyntax() != null) { 994 // Try to inherit the syntax from the superior type if possible 995 syntax = getSuperiorType().getSyntax(); 996 } else { 997 syntax = schema.getDefaultSyntax(); 998 } 999 1000 if (equalityMatchingRuleOID != null) { 1001 // Use explicitly defined matching rule first. 1002 try { 1003 equalityMatchingRule = schema.getMatchingRule(equalityMatchingRuleOID); 1004 } catch (final UnknownSchemaElementException e) { 1005 final LocalizableMessage message = 1006 WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_EQUALITY_MR1.get(getNameOrOID(), 1007 equalityMatchingRuleOID); 1008 failValidation(invalidSchemaElements, warnings, message); 1009 return false; 1010 } 1011 } else if (getSuperiorType() != null && getSuperiorType().getEqualityMatchingRule() != null) { 1012 // Inherit matching rule from superior type if possible 1013 equalityMatchingRule = getSuperiorType().getEqualityMatchingRule(); 1014 } else if (getSyntax() != null && getSyntax().getEqualityMatchingRule() != null) { 1015 // Use default for syntax 1016 equalityMatchingRule = getSyntax().getEqualityMatchingRule(); 1017 } 1018 1019 if (orderingMatchingRuleOID != null) { 1020 // Use explicitly defined matching rule first. 1021 try { 1022 orderingMatchingRule = schema.getMatchingRule(orderingMatchingRuleOID); 1023 } catch (final UnknownSchemaElementException e) { 1024 final LocalizableMessage message = 1025 WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_ORDERING_MR1.get(getNameOrOID(), 1026 orderingMatchingRuleOID); 1027 failValidation(invalidSchemaElements, warnings, message); 1028 return false; 1029 } 1030 } else if (getSuperiorType() != null && getSuperiorType().getOrderingMatchingRule() != null) { 1031 // Inherit matching rule from superior type if possible 1032 orderingMatchingRule = getSuperiorType().getOrderingMatchingRule(); 1033 } else if (getSyntax() != null && getSyntax().getOrderingMatchingRule() != null) { 1034 // Use default for syntax 1035 orderingMatchingRule = getSyntax().getOrderingMatchingRule(); 1036 } 1037 1038 if (substringMatchingRuleOID != null) { 1039 // Use explicitly defined matching rule first. 1040 try { 1041 substringMatchingRule = schema.getMatchingRule(substringMatchingRuleOID); 1042 } catch (final UnknownSchemaElementException e) { 1043 final LocalizableMessage message = 1044 WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_SUBSTRING_MR1.get(getNameOrOID(), 1045 substringMatchingRuleOID); 1046 failValidation(invalidSchemaElements, warnings, message); 1047 return false; 1048 } 1049 } else if (getSuperiorType() != null 1050 && getSuperiorType().getSubstringMatchingRule() != null) { 1051 // Inherit matching rule from superior type if possible 1052 substringMatchingRule = getSuperiorType().getSubstringMatchingRule(); 1053 } else if (getSyntax() != null && getSyntax().getSubstringMatchingRule() != null) { 1054 // Use default for syntax 1055 substringMatchingRule = getSyntax().getSubstringMatchingRule(); 1056 } 1057 1058 if (approximateMatchingRuleOID != null) { 1059 // Use explicitly defined matching rule first. 1060 try { 1061 approximateMatchingRule = schema.getMatchingRule(approximateMatchingRuleOID); 1062 } catch (final UnknownSchemaElementException e) { 1063 final LocalizableMessage message = 1064 WARN_ATTR_SYNTAX_ATTRTYPE_UNKNOWN_APPROXIMATE_MR1.get(getNameOrOID(), 1065 approximateMatchingRuleOID); 1066 failValidation(invalidSchemaElements, warnings, message); 1067 return false; 1068 } 1069 } else if (getSuperiorType() != null 1070 && getSuperiorType().getApproximateMatchingRule() != null) { 1071 // Inherit matching rule from superior type if possible 1072 approximateMatchingRule = getSuperiorType().getApproximateMatchingRule(); 1073 } else if (getSyntax() != null && getSyntax().getApproximateMatchingRule() != null) { 1074 // Use default for syntax 1075 approximateMatchingRule = getSyntax().getApproximateMatchingRule(); 1076 } 1077 1078 // If the attribute type is COLLECTIVE, then it must have a usage of 1079 // userApplications. 1080 if (isCollective() && getUsage() != AttributeUsage.USER_APPLICATIONS) { 1081 final LocalizableMessage message = 1082 WARN_ATTR_SYNTAX_ATTRTYPE_COLLECTIVE_IS_OPERATIONAL.get(getNameOrOID()); 1083 warnings.add(message); 1084 } 1085 1086 // If the attribute type is NO-USER-MODIFICATION, then it must not 1087 // have a usage of userApplications. 1088 if (isNoUserModification() && getUsage() == AttributeUsage.USER_APPLICATIONS) { 1089 final LocalizableMessage message = 1090 WARN_ATTR_SYNTAX_ATTRTYPE_NO_USER_MOD_NOT_OPERATIONAL.get(getNameOrOID()); 1091 warnings.add(message); 1092 } 1093 1094 return isValid = true; 1095 } 1096 1097 private void failValidation(final List<AttributeType> invalidSchemaElements, 1098 final List<LocalizableMessage> warnings, final LocalizableMessage message) { 1099 invalidSchemaElements.add(this); 1100 warnings.add(ERR_ATTR_TYPE_VALIDATION_FAIL.get(toString(), message)); 1101 } 1102}