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 2015 ForgeRock AS. 026 */ 027 028package org.forgerock.opendj.ldap.schema; 029 030import java.util.Collection; 031import java.util.Collections; 032import java.util.HashSet; 033import java.util.Iterator; 034import java.util.LinkedHashSet; 035import java.util.LinkedList; 036import java.util.List; 037import java.util.Map; 038import java.util.Set; 039 040import org.forgerock.i18n.LocalizableMessage; 041 042import static java.util.Arrays.*; 043import static java.util.Collections.*; 044 045import static org.forgerock.opendj.ldap.schema.ObjectClassType.*; 046import static org.forgerock.opendj.ldap.schema.SchemaConstants.*; 047import static org.forgerock.opendj.ldap.schema.SchemaUtils.*; 048 049import static com.forgerock.opendj.ldap.CoreMessages.*; 050 051/** 052 * This class defines a data structure for storing and interacting with an 053 * objectclass, which contains a collection of attributes that must and/or may 054 * be present in an entry with that objectclass. 055 * <p> 056 * Where ordered sets of names, attribute types, or extra properties are 057 * provided, the ordering will be preserved when the associated fields are 058 * accessed via their getters or via the {@link #toString()} methods. 059 */ 060public final class ObjectClass extends SchemaElement { 061 062 /** A fluent API for incrementally constructing object classes. */ 063 public static final class Builder extends SchemaElementBuilder<Builder> { 064 private boolean isObsolete; 065 private final List<String> names = new LinkedList<>(); 066 private String oid; 067 private final Set<String> optionalAttributes = new LinkedHashSet<>(); 068 private final Set<String> requiredAttributes = new LinkedHashSet<>(); 069 private final Set<String> superiorClasses = new LinkedHashSet<>(); 070 private ObjectClassType type; 071 072 Builder(final ObjectClass oc, final SchemaBuilder builder) { 073 super(builder, oc); 074 this.oid = oc.oid; 075 this.names.addAll(oc.names); 076 this.isObsolete = oc.isObsolete; 077 this.type = oc.objectClassType; 078 this.superiorClasses.addAll(oc.superiorClassOIDs); 079 this.requiredAttributes.addAll(oc.requiredAttributeOIDs); 080 this.optionalAttributes.addAll(optionalAttributes); 081 } 082 083 Builder(final String oid, final SchemaBuilder builder) { 084 super(builder); 085 this.oid = oid; 086 } 087 088 /** 089 * Adds this object class to the schema, throwing a 090 * {@code ConflictingSchemaElementException} if there is an existing 091 * object class with the same numeric OID. 092 * 093 * @return The parent schema builder. 094 * @throws ConflictingSchemaElementException 095 * If there is an existing object class with the same numeric 096 * OID. 097 */ 098 public SchemaBuilder addToSchema() { 099 return getSchemaBuilder().addObjectClass(new ObjectClass(this), false); 100 } 101 102 /** 103 * Adds this object class to the schema overwriting any existing object class 104 * with the same numeric OID. 105 * 106 * @return The parent schema builder. 107 */ 108 public SchemaBuilder addToSchemaOverwrite() { 109 return getSchemaBuilder().addObjectClass(new ObjectClass(this), true); 110 } 111 112 @Override 113 public Builder description(final String description) { 114 return description0(description); 115 } 116 117 @Override 118 public Builder extraProperties(final Map<String, List<String>> extraProperties) { 119 return extraProperties0(extraProperties); 120 } 121 122 @Override 123 public Builder extraProperties(final String extensionName, final String... extensionValues) { 124 return extraProperties0(extensionName, extensionValues); 125 } 126 127 @Override 128 Builder getThis() { 129 return this; 130 } 131 132 /** 133 * Adds the provided user friendly names. 134 * 135 * @param names 136 * The user friendly names. 137 * @return This builder. 138 */ 139 public Builder names(final Collection<String> names) { 140 this.names.addAll(names); 141 return this; 142 } 143 144 /** 145 * Adds the provided user friendly names. 146 * 147 * @param names 148 * The user friendly names. 149 * @return This builder. 150 */ 151 public Builder names(final String... names) { 152 return names(asList(names)); 153 } 154 155 /** 156 * Specifies whether this schema element is obsolete. 157 * 158 * @param isObsolete 159 * {@code true} if this schema element is obsolete 160 * (default is {@code false}). 161 * @return This builder. 162 */ 163 public Builder obsolete(final boolean isObsolete) { 164 this.isObsolete = isObsolete; 165 return this; 166 } 167 168 /** 169 * Sets the numeric OID which uniquely identifies this object class. 170 * 171 * @param oid 172 * The numeric OID. 173 * @return This builder. 174 */ 175 public Builder oid(final String oid) { 176 this.oid = oid; 177 return this; 178 } 179 180 /** 181 * Adds the provided optional attributes. 182 * 183 * @param attributeNamesOrOIDs 184 * The list of optional attribute names or OIDs. 185 * @return This builder. 186 */ 187 public Builder optionalAttributes(final Collection<String> attributeNamesOrOIDs) { 188 this.optionalAttributes.addAll(attributeNamesOrOIDs); 189 return this; 190 } 191 192 /** 193 * Adds the provided optional attributes. 194 * 195 * @param attributeNamesOrOIDs 196 * The list of optional attribute names or OIDs. 197 * @return This builder. 198 */ 199 public Builder optionalAttributes(final String... attributeNamesOrOIDs) { 200 this.optionalAttributes.addAll(asList(attributeNamesOrOIDs)); 201 return this; 202 } 203 204 @Override 205 public Builder removeAllExtraProperties() { 206 return removeAllExtraProperties0(); 207 } 208 209 @Override 210 public Builder removeExtraProperty(final String extensionName, final String... extensionValues) { 211 return removeExtraProperty0(extensionName, extensionValues); 212 } 213 214 /** 215 * Removes all user defined names. 216 * 217 * @return This builder. 218 */ 219 public Builder removeAllNames() { 220 this.names.clear(); 221 return this; 222 } 223 224 /** 225 * Removes all optional attributes. 226 * 227 * @return This builder. 228 */ 229 public Builder removeAllOptionalAttributes() { 230 this.optionalAttributes.clear(); 231 return this; 232 } 233 234 /** 235 * Removes all required attributes. 236 * 237 * @return This builder. 238 */ 239 public Builder removeAllRequiredAttributes() { 240 this.requiredAttributes.clear(); 241 return this; 242 } 243 244 /** 245 * Removes all superior object class. 246 * 247 * @return This builder. 248 */ 249 public Builder removeAllSuperiorObjectClass() { 250 this.superiorClasses.clear(); 251 return this; 252 } 253 254 /** 255 * Removes the provided user defined name. 256 * 257 * @param name 258 * The user defined name to be removed. 259 * @return This builder. 260 */ 261 public Builder removeName(String name) { 262 this.names.remove(name); 263 return this; 264 } 265 266 /** 267 * Removes the provided optional attribute. 268 * 269 * @param attributeNameOrOID 270 * The optional attribute name or OID to be removed. 271 * @return This builder. 272 */ 273 public Builder removeOptionalAttribute(String attributeNameOrOID) { 274 this.optionalAttributes.remove(attributeNameOrOID); 275 return this; 276 } 277 278 /** 279 * Removes the provided required attribute. 280 * 281 * @param attributeNameOrOID 282 * The provided required attribute name or OID to be removed. 283 * @return This builder. 284 */ 285 public Builder removeRequiredAttribute(String attributeNameOrOID) { 286 this.requiredAttributes.remove(attributeNameOrOID); 287 return this; 288 } 289 290 /** 291 * Removes the provided superior object class. 292 * 293 * @param objectClassNameOrOID 294 * The superior object class name or OID to be removed. 295 * @return This builder. 296 */ 297 public Builder removeSuperiorObjectClass(String objectClassNameOrOID) { 298 this.superiorClasses.remove(objectClassNameOrOID); 299 return this; 300 } 301 302 /** 303 * Adds the provided required attributes. 304 * 305 * @param attributeNamesOrOIDs 306 * The list of required attribute names or OIDs. 307 * @return This builder. 308 */ 309 public Builder requiredAttributes(final Collection<String> attributeNamesOrOIDs) { 310 this.requiredAttributes.addAll(attributeNamesOrOIDs); 311 return this; 312 } 313 314 /** 315 * Adds the provided required attributes. 316 * 317 * @param attributeNamesOrOIDs 318 * The list of required attribute names or OIDs. 319 * @return This builder. 320 */ 321 public Builder requiredAttributes(final String... attributeNamesOrOIDs) { 322 this.requiredAttributes.addAll(asList(attributeNamesOrOIDs)); 323 return this; 324 } 325 326 /** 327 * Adds the provided superior object classes. 328 * 329 * @param objectClassNamesOrOIDs 330 * The list of superior object classes names or OIDs. 331 * @return This builder. 332 */ 333 public Builder superiorObjectClasses(final Collection<String> objectClassNamesOrOIDs) { 334 this.superiorClasses.addAll(objectClassNamesOrOIDs); 335 return this; 336 } 337 338 /** 339 * Adds the provided superior object classes. 340 * 341 * @param objectClassNamesOrOIDs 342 * The list of superior object classes names or OIDs. 343 * @return This builder. 344 */ 345 public Builder superiorObjectClasses(final String... objectClassNamesOrOIDs) { 346 this.superiorClasses.addAll(asList(objectClassNamesOrOIDs)); 347 return this; 348 } 349 350 /** 351 * Sets the type of this object class. 352 * 353 * @param type 354 * The object class type. 355 * @return This builder. 356 */ 357 public Builder type(final ObjectClassType type) { 358 this.type = type; 359 return this; 360 } 361 } 362 363 /** The OID that may be used to reference this definition. */ 364 private final String oid; 365 366 /** The set of user defined names for this definition. */ 367 private final List<String> names; 368 369 /** Indicates whether this definition is declared "obsolete". */ 370 private final boolean isObsolete; 371 372 /** The reference to the superior objectclasses. */ 373 private final Set<String> superiorClassOIDs; 374 375 /** The objectclass type for this objectclass. */ 376 private final ObjectClassType objectClassType; 377 378 /** The set of required attribute types for this objectclass. */ 379 private final Set<String> requiredAttributeOIDs; 380 381 /** The set of optional attribute types for this objectclass. */ 382 private final Set<String> optionalAttributeOIDs; 383 384 private Set<ObjectClass> superiorClasses = emptySet(); 385 private Set<AttributeType> declaredRequiredAttributes = emptySet(); 386 private Set<AttributeType> requiredAttributes = emptySet(); 387 private Set<AttributeType> declaredOptionalAttributes = emptySet(); 388 private Set<AttributeType> optionalAttributes = emptySet(); 389 390 /** Indicates whether or not validation has been performed. */ 391 private boolean needsValidating = true; 392 393 /** The indicates whether or not validation failed. */ 394 private boolean isValid; 395 396 /** 397 * Construct a extensibleObject object class where the set of allowed 398 * attribute types of this object class is implicitly the set of all 399 * attribute types of userApplications usage. 400 * 401 * @param description 402 * The description for this schema definition 403 * @param extraProperties 404 * The map of "extra" properties for this schema definition 405 */ 406 static ObjectClass newExtensibleObjectObjectClass(final String description, 407 final Map<String, List<String>> extraProperties, final SchemaBuilder builder) { 408 return new ObjectClass(new Builder(EXTENSIBLE_OBJECT_OBJECTCLASS_OID, builder) 409 .description(description) 410 .extraProperties(extraProperties) 411 .names(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME) 412 .superiorObjectClasses(TOP_OBJECTCLASS_NAME) 413 .type(AUXILIARY)); 414 } 415 416 417 private ObjectClass(final Builder builder) { 418 super(builder); 419 420 if (builder.oid == null || builder.oid.isEmpty()) { 421 throw new IllegalArgumentException("An OID must be specified."); 422 } 423 424 this.oid = builder.oid; 425 this.names = unmodifiableCopyOfList(builder.names); 426 this.isObsolete = builder.isObsolete; 427 this.superiorClassOIDs = unmodifiableCopyOfSet(builder.superiorClasses); 428 this.objectClassType = builder.type; 429 this.requiredAttributeOIDs = unmodifiableCopyOfSet(builder.requiredAttributes); 430 this.optionalAttributeOIDs = unmodifiableCopyOfSet(builder.optionalAttributes); 431 } 432 433 /** 434 * Returns {@code true} if the provided object is an object class having the 435 * same numeric OID as this object class. 436 * 437 * @param o 438 * The object to be compared. 439 * @return {@code true} if the provided object is a object class having the 440 * same numeric OID as this object class. 441 */ 442 @Override 443 public boolean equals(final Object o) { 444 if (this == o) { 445 return true; 446 } else if (o instanceof ObjectClass) { 447 final ObjectClass other = (ObjectClass) o; 448 return oid.equals(other.oid); 449 } else { 450 return false; 451 } 452 } 453 454 /** 455 * Returns an unmodifiable set containing the optional attributes for this 456 * object class. Note that this set will not automatically include any 457 * optional attributes for superior object classes. 458 * 459 * @return An unmodifiable set containing the optional attributes for this 460 * object class. 461 */ 462 public Set<AttributeType> getDeclaredOptionalAttributes() { 463 return declaredOptionalAttributes; 464 } 465 466 /** 467 * Returns an unmodifiable set containing the required attributes for this 468 * object class. Note that this set will not automatically include any 469 * required attributes for superior object classes. 470 * 471 * @return An unmodifiable set containing the required attributes for this 472 * object class. 473 */ 474 public Set<AttributeType> getDeclaredRequiredAttributes() { 475 return declaredRequiredAttributes; 476 } 477 478 /** 479 * Returns the name or OID for this schema definition. If it has one or more 480 * names, then the primary name will be returned. If it does not have any 481 * names, then the OID will be returned. 482 * 483 * @return The name or OID for this schema definition. 484 */ 485 public String getNameOrOID() { 486 if (names.isEmpty()) { 487 return oid; 488 } 489 return names.get(0); 490 } 491 492 /** 493 * Returns an unmodifiable list containing the user-defined names that may 494 * be used to reference this schema definition. 495 * 496 * @return Returns an unmodifiable list containing the user-defined names 497 * that may be used to reference this schema definition. 498 */ 499 public List<String> getNames() { 500 return names; 501 } 502 503 /** 504 * Returns the objectclass type for this objectclass. 505 * 506 * @return The objectclass type for this objectclass. 507 */ 508 public ObjectClassType getObjectClassType() { 509 return objectClassType != null ? objectClassType : STRUCTURAL; 510 } 511 512 /** 513 * Returns the OID for this schema definition. 514 * 515 * @return The OID for this schema definition. 516 */ 517 public String getOID() { 518 return oid; 519 } 520 521 /** 522 * Returns an unmodifiable set containing the optional attributes for this 523 * object class and any superior object classes that it might have. 524 * 525 * @return An unmodifiable set containing the optional attributes for this 526 * object class and any superior object classes that it might have. 527 */ 528 public Set<AttributeType> getOptionalAttributes() { 529 return optionalAttributes; 530 } 531 532 /** 533 * Returns an unmodifiable set containing the required attributes for this 534 * object class and any superior object classes that it might have. 535 * 536 * @return An unmodifiable set containing the required attributes for this 537 * object class and any superior object classes that it might have. 538 */ 539 public Set<AttributeType> getRequiredAttributes() { 540 return requiredAttributes; 541 } 542 543 /** 544 * Returns an unmodifiable set containing the superior classes for this 545 * object class. 546 * 547 * @return An unmodifiable set containing the superior classes for this 548 * object class. 549 */ 550 public Set<ObjectClass> getSuperiorClasses() { 551 return superiorClasses; 552 } 553 554 /** 555 * Returns the hash code for this object class. It will be calculated as the 556 * hash code of the numeric OID. 557 * 558 * @return The hash code for this object class. 559 */ 560 @Override 561 public int hashCode() { 562 return oid.hashCode(); 563 } 564 565 /** 566 * Indicates whether this schema definition has the specified name. 567 * 568 * @param name 569 * The name for which to make the determination. 570 * @return <code>true</code> if the specified name is assigned to this 571 * schema definition, or <code>false</code> if not. 572 */ 573 public boolean hasName(final String name) { 574 for (final String n : names) { 575 if (n.equalsIgnoreCase(name)) { 576 return true; 577 } 578 } 579 return false; 580 } 581 582 /** 583 * Indicates whether this schema definition has the specified name or OID. 584 * 585 * @param value 586 * The value for which to make the determination. 587 * @return <code>true</code> if the provided value matches the OID or one of 588 * the names assigned to this schema definition, or 589 * <code>false</code> if not. 590 */ 591 public boolean hasNameOrOID(final String value) { 592 return hasName(value) || getOID().equals(value); 593 } 594 595 /** 596 * Indicates whether this objectclass is a descendant of the provided class. 597 * 598 * @param objectClass 599 * The objectClass for which to make the determination. 600 * @return <code>true</code> if this objectclass is a descendant of the 601 * provided class, or <code>false</code> if not. 602 */ 603 public boolean isDescendantOf(final ObjectClass objectClass) { 604 for (final ObjectClass sup : superiorClasses) { 605 if (sup.equals(objectClass) || sup.isDescendantOf(objectClass)) { 606 return true; 607 } 608 } 609 return false; 610 } 611 612 /** 613 * Indicates whether this schema definition is declared "obsolete". 614 * 615 * @return <code>true</code> if this schema definition is declared 616 * "obsolete", or <code>false</code> if not. 617 */ 618 public boolean isObsolete() { 619 return isObsolete; 620 } 621 622 /** 623 * Indicates whether the provided attribute type is included in the optional 624 * attribute list for this or any of its superior objectclasses. 625 * 626 * @param attributeType 627 * The attribute type for which to make the determination. 628 * @return <code>true</code> if the provided attribute type is optional for 629 * this objectclass or any of its superior classes, or 630 * <code>false</code> if not. 631 */ 632 public boolean isOptional(final AttributeType attributeType) { 633 return optionalAttributes.contains(attributeType); 634 } 635 636 /** 637 * Indicates whether the provided attribute type is included in the required 638 * attribute list for this or any of its superior objectclasses. 639 * 640 * @param attributeType 641 * The attribute type for which to make the determination. 642 * @return <code>true</code> if the provided attribute type is required by 643 * this objectclass or any of its superior classes, or 644 * <code>false</code> if not. 645 */ 646 public boolean isRequired(final AttributeType attributeType) { 647 return requiredAttributes.contains(attributeType); 648 } 649 650 /** 651 * Indicates whether the provided attribute type is in the list of required 652 * or optional attributes for this objectclass or any of its superior 653 * classes. 654 * 655 * @param attributeType 656 * The attribute type for which to make the determination. 657 * @return <code>true</code> if the provided attribute type is required or 658 * allowed for this objectclass or any of its superior classes, or 659 * <code>false</code> if it is not. 660 */ 661 public boolean isRequiredOrOptional(final AttributeType attributeType) { 662 return isRequired(attributeType) || isOptional(attributeType); 663 } 664 665 @Override 666 void toStringContent(final StringBuilder buffer) { 667 buffer.append(oid); 668 669 if (!names.isEmpty()) { 670 final Iterator<String> iterator = names.iterator(); 671 672 final String firstName = iterator.next(); 673 if (iterator.hasNext()) { 674 buffer.append(" NAME ( '"); 675 buffer.append(firstName); 676 677 while (iterator.hasNext()) { 678 buffer.append("' '"); 679 buffer.append(iterator.next()); 680 } 681 682 buffer.append("' )"); 683 } else { 684 buffer.append(" NAME '"); 685 buffer.append(firstName); 686 buffer.append("'"); 687 } 688 } 689 690 appendDescription(buffer); 691 692 if (isObsolete) { 693 buffer.append(" OBSOLETE"); 694 } 695 696 if (!superiorClassOIDs.isEmpty()) { 697 final Iterator<String> iterator = superiorClassOIDs.iterator(); 698 699 final String firstName = iterator.next(); 700 if (iterator.hasNext()) { 701 buffer.append(" SUP ( "); 702 buffer.append(firstName); 703 704 while (iterator.hasNext()) { 705 buffer.append(" $ "); 706 buffer.append(iterator.next()); 707 } 708 709 buffer.append(" )"); 710 } else { 711 buffer.append(" SUP "); 712 buffer.append(firstName); 713 } 714 } 715 716 if (objectClassType != null) { 717 buffer.append(" "); 718 buffer.append(objectClassType); 719 } 720 721 if (!requiredAttributeOIDs.isEmpty()) { 722 final Iterator<String> iterator = requiredAttributeOIDs.iterator(); 723 724 final String firstName = iterator.next(); 725 if (iterator.hasNext()) { 726 buffer.append(" MUST ( "); 727 buffer.append(firstName); 728 729 while (iterator.hasNext()) { 730 buffer.append(" $ "); 731 buffer.append(iterator.next()); 732 } 733 734 buffer.append(" )"); 735 } else { 736 buffer.append(" MUST "); 737 buffer.append(firstName); 738 } 739 } 740 741 if (!optionalAttributeOIDs.isEmpty()) { 742 final Iterator<String> iterator = optionalAttributeOIDs.iterator(); 743 744 final String firstName = iterator.next(); 745 if (iterator.hasNext()) { 746 buffer.append(" MAY ( "); 747 buffer.append(firstName); 748 749 while (iterator.hasNext()) { 750 buffer.append(" $ "); 751 buffer.append(iterator.next()); 752 } 753 754 buffer.append(" )"); 755 } else { 756 buffer.append(" MAY "); 757 buffer.append(firstName); 758 } 759 } 760 } 761 762 boolean validate(final Schema schema, final List<ObjectClass> invalidSchemaElements, 763 final List<LocalizableMessage> warnings) { 764 // Avoid validating this schema element more than once. 765 // This may occur if multiple object classes specify the same superior. 766 if (!needsValidating) { 767 return isValid; 768 } 769 770 // Prevent re-validation. 771 needsValidating = false; 772 773 // Init a flag to check to inheritance from top (only needed for 774 // structural object classes) per RFC 4512 775 boolean derivesTop = getObjectClassType() != ObjectClassType.STRUCTURAL; 776 777 if (!superiorClassOIDs.isEmpty()) { 778 superiorClasses = new HashSet<>(superiorClassOIDs.size()); 779 ObjectClass superiorClass; 780 for (final String superClassOid : superiorClassOIDs) { 781 try { 782 superiorClass = schema.getObjectClass(superClassOid); 783 } catch (final UnknownSchemaElementException e) { 784 final LocalizableMessage message = 785 WARN_ATTR_SYNTAX_OBJECTCLASS_UNKNOWN_SUPERIOR_CLASS1.get( 786 getNameOrOID(), superClassOid); 787 failValidation(invalidSchemaElements, warnings, message); 788 return false; 789 } 790 791 // Make sure that the inheritance configuration is acceptable. 792 final ObjectClassType superiorType = superiorClass.getObjectClassType(); 793 final ObjectClassType type = getObjectClassType(); 794 switch (type) { 795 case ABSTRACT: 796 // Abstract classes may only inherit from other abstract classes. 797 if (superiorType != ObjectClassType.ABSTRACT) { 798 final LocalizableMessage message = 799 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get( 800 getNameOrOID(), type.toString(), superiorType 801 .toString(), superiorClass.getNameOrOID()); 802 failValidation(invalidSchemaElements, warnings, message); 803 return false; 804 } 805 break; 806 807 case AUXILIARY: 808 // Auxiliary classes may only inherit from abstract classes 809 // or other auxiliary classes. 810 if (superiorType != ObjectClassType.ABSTRACT 811 && superiorType != ObjectClassType.AUXILIARY) { 812 final LocalizableMessage message = 813 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get( 814 getNameOrOID(), type.toString(), superiorType 815 .toString(), superiorClass.getNameOrOID()); 816 failValidation(invalidSchemaElements, warnings, message); 817 return false; 818 } 819 break; 820 821 case STRUCTURAL: 822 // Structural classes may only inherit from abstract classes 823 // or other structural classes. 824 if (superiorType != ObjectClassType.ABSTRACT 825 && superiorType != ObjectClassType.STRUCTURAL) { 826 final LocalizableMessage message = 827 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get( 828 getNameOrOID(), type.toString(), superiorType 829 .toString(), superiorClass.getNameOrOID()); 830 failValidation(invalidSchemaElements, warnings, message); 831 return false; 832 } 833 break; 834 } 835 836 // All existing structural object classes defined in this schema 837 // are implicitly guaranteed to inherit from top. 838 if (!derivesTop && superiorType == ObjectClassType.STRUCTURAL) { 839 derivesTop = true; 840 } 841 842 // First ensure that the superior has been validated and fail if 843 // it is invalid. 844 if (!superiorClass.validate(schema, invalidSchemaElements, warnings)) { 845 final LocalizableMessage message = 846 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_CLASS.get(getNameOrOID(), 847 superClassOid); 848 failValidation(invalidSchemaElements, warnings, message); 849 return false; 850 } 851 852 // Inherit all required attributes from superior class. 853 Iterator<AttributeType> i = superiorClass.getRequiredAttributes().iterator(); 854 if (i.hasNext() && requiredAttributes == Collections.EMPTY_SET) { 855 requiredAttributes = new HashSet<>(); 856 } 857 while (i.hasNext()) { 858 requiredAttributes.add(i.next()); 859 } 860 861 // Inherit all optional attributes from superior class. 862 i = superiorClass.getRequiredAttributes().iterator(); 863 if (i.hasNext() && requiredAttributes == Collections.EMPTY_SET) { 864 requiredAttributes = new HashSet<>(); 865 } 866 while (i.hasNext()) { 867 requiredAttributes.add(i.next()); 868 } 869 870 superiorClasses.add(superiorClass); 871 } 872 } 873 874 if (!derivesTop) { 875 derivesTop = isDescendantOf(schema.getObjectClass("2.5.6.0")); 876 } 877 878 // Structural classes must have the "top" objectclass somewhere 879 // in the superior chain. 880 if (!derivesTop) { 881 final LocalizableMessage message = 882 WARN_ATTR_SYNTAX_OBJECTCLASS_STRUCTURAL_SUPERIOR_NOT_TOP1.get(getNameOrOID()); 883 failValidation(invalidSchemaElements, warnings, message); 884 return false; 885 } 886 887 if (oid.equals(EXTENSIBLE_OBJECT_OBJECTCLASS_OID)) { 888 declaredOptionalAttributes = new HashSet<>(requiredAttributeOIDs.size()); 889 for (final AttributeType attributeType : schema.getAttributeTypes()) { 890 if (attributeType.getUsage() == AttributeUsage.USER_APPLICATIONS) { 891 declaredOptionalAttributes.add(attributeType); 892 } 893 } 894 optionalAttributes = declaredRequiredAttributes; 895 } else { 896 if (!requiredAttributeOIDs.isEmpty()) { 897 declaredRequiredAttributes = new HashSet<>(requiredAttributeOIDs.size()); 898 AttributeType attributeType; 899 for (final String requiredAttribute : requiredAttributeOIDs) { 900 try { 901 attributeType = schema.getAttributeType(requiredAttribute); 902 } catch (final UnknownSchemaElementException e) { 903 // This isn't good because it means that the objectclass 904 // requires an attribute type that we don't know anything about. 905 final LocalizableMessage message = 906 WARN_ATTR_SYNTAX_OBJECTCLASS_UNKNOWN_REQUIRED_ATTR1.get( 907 getNameOrOID(), requiredAttribute); 908 failValidation(invalidSchemaElements, warnings, message); 909 return false; 910 } 911 declaredRequiredAttributes.add(attributeType); 912 } 913 if (requiredAttributes == Collections.EMPTY_SET) { 914 requiredAttributes = declaredRequiredAttributes; 915 } else { 916 requiredAttributes.addAll(declaredRequiredAttributes); 917 } 918 } 919 920 if (!optionalAttributeOIDs.isEmpty()) { 921 declaredOptionalAttributes = new HashSet<>(optionalAttributeOIDs.size()); 922 AttributeType attributeType; 923 for (final String optionalAttribute : optionalAttributeOIDs) { 924 try { 925 attributeType = schema.getAttributeType(optionalAttribute); 926 } catch (final UnknownSchemaElementException e) { 927 // This isn't good because it means that the objectclass 928 // requires an attribute type that we don't know anything about. 929 final LocalizableMessage message = 930 WARN_ATTR_SYNTAX_OBJECTCLASS_UNKNOWN_OPTIONAL_ATTR1.get( 931 getNameOrOID(), optionalAttribute); 932 failValidation(invalidSchemaElements, warnings, message); 933 return false; 934 } 935 declaredOptionalAttributes.add(attributeType); 936 } 937 if (optionalAttributes == Collections.EMPTY_SET) { 938 optionalAttributes = declaredOptionalAttributes; 939 } else { 940 optionalAttributes.addAll(declaredOptionalAttributes); 941 } 942 } 943 } 944 945 declaredOptionalAttributes = Collections.unmodifiableSet(declaredOptionalAttributes); 946 declaredRequiredAttributes = Collections.unmodifiableSet(declaredRequiredAttributes); 947 optionalAttributes = Collections.unmodifiableSet(optionalAttributes); 948 requiredAttributes = Collections.unmodifiableSet(requiredAttributes); 949 superiorClasses = Collections.unmodifiableSet(superiorClasses); 950 951 return isValid = true; 952 } 953 954 private void failValidation(final List<ObjectClass> invalidSchemaElements, 955 final List<LocalizableMessage> warnings, final LocalizableMessage message) { 956 invalidSchemaElements.add(this); 957 warnings.add(ERR_OC_VALIDATION_FAIL.get(toString(), message)); 958 } 959}