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