001/* 002 * Copyright 2007-2018 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2018 Ping Identity Corporation 007 * 008 * This program is free software; you can redistribute it and/or modify 009 * it under the terms of the GNU General Public License (GPLv2 only) 010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only) 011 * as published by the Free Software Foundation. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 016 * GNU General Public License for more details. 017 * 018 * You should have received a copy of the GNU General Public License 019 * along with this program; if not, see <http://www.gnu.org/licenses>. 020 */ 021package com.unboundid.ldap.sdk.schema; 022 023 024 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.HashSet; 029import java.util.Map; 030import java.util.LinkedHashMap; 031import java.util.LinkedHashSet; 032import java.util.Set; 033 034import com.unboundid.ldap.sdk.LDAPException; 035import com.unboundid.ldap.sdk.ResultCode; 036import com.unboundid.util.NotMutable; 037import com.unboundid.util.ThreadSafety; 038import com.unboundid.util.ThreadSafetyLevel; 039 040import static com.unboundid.ldap.sdk.schema.SchemaMessages.*; 041import static com.unboundid.util.StaticUtils.*; 042import static com.unboundid.util.Validator.*; 043 044 045 046/** 047 * This class provides a data structure that describes an LDAP object class 048 * schema element. 049 */ 050@NotMutable() 051@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 052public final class ObjectClassDefinition 053 extends SchemaElement 054{ 055 /** 056 * The serial version UID for this serializable class. 057 */ 058 private static final long serialVersionUID = -3024333376249332728L; 059 060 061 062 // Indicates whether this object class is declared obsolete. 063 private final boolean isObsolete; 064 065 // The set of extensions for this object class. 066 private final Map<String,String[]> extensions; 067 068 // The object class type for this object class. 069 private final ObjectClassType objectClassType; 070 071 // The description for this object class. 072 private final String description; 073 074 // The string representation of this object class. 075 private final String objectClassString; 076 077 // The OID for this object class. 078 private final String oid; 079 080 // The set of names for this object class. 081 private final String[] names; 082 083 // The names/OIDs of the optional attributes. 084 private final String[] optionalAttributes; 085 086 // The names/OIDs of the required attributes. 087 private final String[] requiredAttributes; 088 089 // The set of superior object class names/OIDs. 090 private final String[] superiorClasses; 091 092 093 094 /** 095 * Creates a new object class from the provided string representation. 096 * 097 * @param s The string representation of the object class to create, using 098 * the syntax described in RFC 4512 section 4.1.1. It must not be 099 * {@code null}. 100 * 101 * @throws LDAPException If the provided string cannot be decoded as an 102 * object class definition. 103 */ 104 public ObjectClassDefinition(final String s) 105 throws LDAPException 106 { 107 ensureNotNull(s); 108 109 objectClassString = s.trim(); 110 111 // The first character must be an opening parenthesis. 112 final int length = objectClassString.length(); 113 if (length == 0) 114 { 115 throw new LDAPException(ResultCode.DECODING_ERROR, 116 ERR_OC_DECODE_EMPTY.get()); 117 } 118 else if (objectClassString.charAt(0) != '(') 119 { 120 throw new LDAPException(ResultCode.DECODING_ERROR, 121 ERR_OC_DECODE_NO_OPENING_PAREN.get( 122 objectClassString)); 123 } 124 125 126 // Skip over any spaces until we reach the start of the OID, then read the 127 // OID until we find the next space. 128 int pos = skipSpaces(objectClassString, 1, length); 129 130 StringBuilder buffer = new StringBuilder(); 131 pos = readOID(objectClassString, pos, length, buffer); 132 oid = buffer.toString(); 133 134 135 // Technically, object class elements are supposed to appear in a specific 136 // order, but we'll be lenient and allow remaining elements to come in any 137 // order. 138 final ArrayList<String> nameList = new ArrayList<String>(1); 139 final ArrayList<String> supList = new ArrayList<String>(1); 140 final ArrayList<String> reqAttrs = new ArrayList<String>(); 141 final ArrayList<String> optAttrs = new ArrayList<String>(); 142 final Map<String,String[]> exts = new LinkedHashMap<String,String[]>(); 143 Boolean obsolete = null; 144 ObjectClassType ocType = null; 145 String descr = null; 146 147 while (true) 148 { 149 // Skip over any spaces until we find the next element. 150 pos = skipSpaces(objectClassString, pos, length); 151 152 // Read until we find the next space or the end of the string. Use that 153 // token to figure out what to do next. 154 final int tokenStartPos = pos; 155 while ((pos < length) && (objectClassString.charAt(pos) != ' ')) 156 { 157 pos++; 158 } 159 160 // It's possible that the token could be smashed right up against the 161 // closing parenthesis. If that's the case, then extract just the token 162 // and handle the closing parenthesis the next time through. 163 String token = objectClassString.substring(tokenStartPos, pos); 164 if ((token.length() > 1) && (token.endsWith(")"))) 165 { 166 token = token.substring(0, token.length() - 1); 167 pos--; 168 } 169 170 final String lowerToken = toLowerCase(token); 171 if (lowerToken.equals(")")) 172 { 173 // This indicates that we're at the end of the value. There should not 174 // be any more closing characters. 175 if (pos < length) 176 { 177 throw new LDAPException(ResultCode.DECODING_ERROR, 178 ERR_OC_DECODE_CLOSE_NOT_AT_END.get( 179 objectClassString)); 180 } 181 break; 182 } 183 else if (lowerToken.equals("name")) 184 { 185 if (nameList.isEmpty()) 186 { 187 pos = skipSpaces(objectClassString, pos, length); 188 pos = readQDStrings(objectClassString, pos, length, nameList); 189 } 190 else 191 { 192 throw new LDAPException(ResultCode.DECODING_ERROR, 193 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 194 objectClassString, "NAME")); 195 } 196 } 197 else if (lowerToken.equals("desc")) 198 { 199 if (descr == null) 200 { 201 pos = skipSpaces(objectClassString, pos, length); 202 203 buffer = new StringBuilder(); 204 pos = readQDString(objectClassString, pos, length, buffer); 205 descr = buffer.toString(); 206 } 207 else 208 { 209 throw new LDAPException(ResultCode.DECODING_ERROR, 210 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 211 objectClassString, "DESC")); 212 } 213 } 214 else if (lowerToken.equals("obsolete")) 215 { 216 if (obsolete == null) 217 { 218 obsolete = true; 219 } 220 else 221 { 222 throw new LDAPException(ResultCode.DECODING_ERROR, 223 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 224 objectClassString, "OBSOLETE")); 225 } 226 } 227 else if (lowerToken.equals("sup")) 228 { 229 if (supList.isEmpty()) 230 { 231 pos = skipSpaces(objectClassString, pos, length); 232 pos = readOIDs(objectClassString, pos, length, supList); 233 } 234 else 235 { 236 throw new LDAPException(ResultCode.DECODING_ERROR, 237 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 238 objectClassString, "SUP")); 239 } 240 } 241 else if (lowerToken.equals("abstract")) 242 { 243 if (ocType == null) 244 { 245 ocType = ObjectClassType.ABSTRACT; 246 } 247 else 248 { 249 throw new LDAPException(ResultCode.DECODING_ERROR, 250 ERR_OC_DECODE_MULTIPLE_OC_TYPES.get( 251 objectClassString)); 252 } 253 } 254 else if (lowerToken.equals("structural")) 255 { 256 if (ocType == null) 257 { 258 ocType = ObjectClassType.STRUCTURAL; 259 } 260 else 261 { 262 throw new LDAPException(ResultCode.DECODING_ERROR, 263 ERR_OC_DECODE_MULTIPLE_OC_TYPES.get( 264 objectClassString)); 265 } 266 } 267 else if (lowerToken.equals("auxiliary")) 268 { 269 if (ocType == null) 270 { 271 ocType = ObjectClassType.AUXILIARY; 272 } 273 else 274 { 275 throw new LDAPException(ResultCode.DECODING_ERROR, 276 ERR_OC_DECODE_MULTIPLE_OC_TYPES.get( 277 objectClassString)); 278 } 279 } 280 else if (lowerToken.equals("must")) 281 { 282 if (reqAttrs.isEmpty()) 283 { 284 pos = skipSpaces(objectClassString, pos, length); 285 pos = readOIDs(objectClassString, pos, length, reqAttrs); 286 } 287 else 288 { 289 throw new LDAPException(ResultCode.DECODING_ERROR, 290 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 291 objectClassString, "MUST")); 292 } 293 } 294 else if (lowerToken.equals("may")) 295 { 296 if (optAttrs.isEmpty()) 297 { 298 pos = skipSpaces(objectClassString, pos, length); 299 pos = readOIDs(objectClassString, pos, length, optAttrs); 300 } 301 else 302 { 303 throw new LDAPException(ResultCode.DECODING_ERROR, 304 ERR_OC_DECODE_MULTIPLE_ELEMENTS.get( 305 objectClassString, "MAY")); 306 } 307 } 308 else if (lowerToken.startsWith("x-")) 309 { 310 pos = skipSpaces(objectClassString, pos, length); 311 312 final ArrayList<String> valueList = new ArrayList<String>(); 313 pos = readQDStrings(objectClassString, pos, length, valueList); 314 315 final String[] values = new String[valueList.size()]; 316 valueList.toArray(values); 317 318 if (exts.containsKey(token)) 319 { 320 throw new LDAPException(ResultCode.DECODING_ERROR, 321 ERR_OC_DECODE_DUP_EXT.get(objectClassString, 322 token)); 323 } 324 325 exts.put(token, values); 326 } 327 else 328 { 329 throw new LDAPException(ResultCode.DECODING_ERROR, 330 ERR_OC_DECODE_UNEXPECTED_TOKEN.get( 331 objectClassString, token)); 332 } 333 } 334 335 description = descr; 336 337 names = new String[nameList.size()]; 338 nameList.toArray(names); 339 340 superiorClasses = new String[supList.size()]; 341 supList.toArray(superiorClasses); 342 343 requiredAttributes = new String[reqAttrs.size()]; 344 reqAttrs.toArray(requiredAttributes); 345 346 optionalAttributes = new String[optAttrs.size()]; 347 optAttrs.toArray(optionalAttributes); 348 349 isObsolete = (obsolete != null); 350 351 objectClassType = ocType; 352 353 extensions = Collections.unmodifiableMap(exts); 354 } 355 356 357 358 /** 359 * Creates a new object class with the provided information. 360 * 361 * @param oid The OID for this object class. It must not be 362 * {@code null}. 363 * @param name The name for this object class. It may be 364 * {@code null} if the object class should only be 365 * referenced by OID. 366 * @param description The description for this object class. It may 367 * be {@code null} if there is no description. 368 * @param superiorClass The name/OID of the superior class for this 369 * object class. It may be {@code null} or 370 * empty if there is no superior class. 371 * @param objectClassType The object class type for this object class. 372 * @param requiredAttributes The names/OIDs of the attributes which must be 373 * present in entries containing this object 374 * class. 375 * @param optionalAttributes The names/OIDs of the attributes which may be 376 * present in entries containing this object 377 * class. 378 * @param extensions The set of extensions for this object class. 379 * It may be {@code null} or empty if there should 380 * not be any extensions. 381 */ 382 public ObjectClassDefinition(final String oid, final String name, 383 final String description, 384 final String superiorClass, 385 final ObjectClassType objectClassType, 386 final String[] requiredAttributes, 387 final String[] optionalAttributes, 388 final Map<String,String[]> extensions) 389 { 390 this(oid, ((name == null) ? null : new String[] { name }), description, 391 false, 392 ((superiorClass == null) ? null : new String[] { superiorClass }), 393 objectClassType, requiredAttributes, optionalAttributes, 394 extensions); 395 } 396 397 398 399 /** 400 * Creates a new object class with the provided information. 401 * 402 * @param oid The OID for this object class. It must not be 403 * {@code null}. 404 * @param name The name for this object class. It may be 405 * {@code null} if the object class should only be 406 * referenced by OID. 407 * @param description The description for this object class. It may 408 * be {@code null} if there is no description. 409 * @param superiorClass The name/OID of the superior class for this 410 * object class. It may be {@code null} or 411 * empty if there is no superior class. 412 * @param objectClassType The object class type for this object class. 413 * @param requiredAttributes The names/OIDs of the attributes which must be 414 * present in entries containing this object 415 * class. 416 * @param optionalAttributes The names/OIDs of the attributes which may be 417 * present in entries containing this object 418 * class. 419 * @param extensions The set of extensions for this object class. 420 * It may be {@code null} or empty if there should 421 * not be any extensions. 422 */ 423 public ObjectClassDefinition(final String oid, final String name, 424 final String description, 425 final String superiorClass, 426 final ObjectClassType objectClassType, 427 final Collection<String> requiredAttributes, 428 final Collection<String> optionalAttributes, 429 final Map<String,String[]> extensions) 430 { 431 this(oid, ((name == null) ? null : new String[] { name }), description, 432 false, 433 ((superiorClass == null) ? null : new String[] { superiorClass }), 434 objectClassType, toArray(requiredAttributes), 435 toArray(optionalAttributes), extensions); 436 } 437 438 439 440 /** 441 * Creates a new object class with the provided information. 442 * 443 * @param oid The OID for this object class. It must not be 444 * {@code null}. 445 * @param names The set of names for this object class. It may 446 * be {@code null} or empty if the object class 447 * should only be referenced by OID. 448 * @param description The description for this object class. It may 449 * be {@code null} if there is no description. 450 * @param isObsolete Indicates whether this object class is declared 451 * obsolete. 452 * @param superiorClasses The names/OIDs of the superior classes for this 453 * object class. It may be {@code null} or 454 * empty if there is no superior class. 455 * @param objectClassType The object class type for this object class. 456 * @param requiredAttributes The names/OIDs of the attributes which must be 457 * present in entries containing this object 458 * class. 459 * @param optionalAttributes The names/OIDs of the attributes which may be 460 * present in entries containing this object 461 * class. 462 * @param extensions The set of extensions for this object class. 463 * It may be {@code null} or empty if there should 464 * not be any extensions. 465 */ 466 public ObjectClassDefinition(final String oid, final String[] names, 467 final String description, 468 final boolean isObsolete, 469 final String[] superiorClasses, 470 final ObjectClassType objectClassType, 471 final String[] requiredAttributes, 472 final String[] optionalAttributes, 473 final Map<String,String[]> extensions) 474 { 475 ensureNotNull(oid); 476 477 this.oid = oid; 478 this.isObsolete = isObsolete; 479 this.description = description; 480 this.objectClassType = objectClassType; 481 482 if (names == null) 483 { 484 this.names = NO_STRINGS; 485 } 486 else 487 { 488 this.names = names; 489 } 490 491 if (superiorClasses == null) 492 { 493 this.superiorClasses = NO_STRINGS; 494 } 495 else 496 { 497 this.superiorClasses = superiorClasses; 498 } 499 500 if (requiredAttributes == null) 501 { 502 this.requiredAttributes = NO_STRINGS; 503 } 504 else 505 { 506 this.requiredAttributes = requiredAttributes; 507 } 508 509 if (optionalAttributes == null) 510 { 511 this.optionalAttributes = NO_STRINGS; 512 } 513 else 514 { 515 this.optionalAttributes = optionalAttributes; 516 } 517 518 if (extensions == null) 519 { 520 this.extensions = Collections.emptyMap(); 521 } 522 else 523 { 524 this.extensions = Collections.unmodifiableMap(extensions); 525 } 526 527 final StringBuilder buffer = new StringBuilder(); 528 createDefinitionString(buffer); 529 objectClassString = buffer.toString(); 530 } 531 532 533 534 /** 535 * Constructs a string representation of this object class definition in the 536 * provided buffer. 537 * 538 * @param buffer The buffer in which to construct a string representation of 539 * this object class definition. 540 */ 541 private void createDefinitionString(final StringBuilder buffer) 542 { 543 buffer.append("( "); 544 buffer.append(oid); 545 546 if (names.length == 1) 547 { 548 buffer.append(" NAME '"); 549 buffer.append(names[0]); 550 buffer.append('\''); 551 } 552 else if (names.length > 1) 553 { 554 buffer.append(" NAME ("); 555 for (final String name : names) 556 { 557 buffer.append(" '"); 558 buffer.append(name); 559 buffer.append('\''); 560 } 561 buffer.append(" )"); 562 } 563 564 if (description != null) 565 { 566 buffer.append(" DESC '"); 567 encodeValue(description, buffer); 568 buffer.append('\''); 569 } 570 571 if (isObsolete) 572 { 573 buffer.append(" OBSOLETE"); 574 } 575 576 if (superiorClasses.length == 1) 577 { 578 buffer.append(" SUP "); 579 buffer.append(superiorClasses[0]); 580 } 581 else if (superiorClasses.length > 1) 582 { 583 buffer.append(" SUP ("); 584 for (int i=0; i < superiorClasses.length; i++) 585 { 586 if (i > 0) 587 { 588 buffer.append(" $ "); 589 } 590 else 591 { 592 buffer.append(' '); 593 } 594 buffer.append(superiorClasses[i]); 595 } 596 buffer.append(" )"); 597 } 598 599 if (objectClassType != null) 600 { 601 buffer.append(' '); 602 buffer.append(objectClassType.getName()); 603 } 604 605 if (requiredAttributes.length == 1) 606 { 607 buffer.append(" MUST "); 608 buffer.append(requiredAttributes[0]); 609 } 610 else if (requiredAttributes.length > 1) 611 { 612 buffer.append(" MUST ("); 613 for (int i=0; i < requiredAttributes.length; i++) 614 { 615 if (i >0) 616 { 617 buffer.append(" $ "); 618 } 619 else 620 { 621 buffer.append(' '); 622 } 623 buffer.append(requiredAttributes[i]); 624 } 625 buffer.append(" )"); 626 } 627 628 if (optionalAttributes.length == 1) 629 { 630 buffer.append(" MAY "); 631 buffer.append(optionalAttributes[0]); 632 } 633 else if (optionalAttributes.length > 1) 634 { 635 buffer.append(" MAY ("); 636 for (int i=0; i < optionalAttributes.length; i++) 637 { 638 if (i > 0) 639 { 640 buffer.append(" $ "); 641 } 642 else 643 { 644 buffer.append(' '); 645 } 646 buffer.append(optionalAttributes[i]); 647 } 648 buffer.append(" )"); 649 } 650 651 for (final Map.Entry<String,String[]> e : extensions.entrySet()) 652 { 653 final String name = e.getKey(); 654 final String[] values = e.getValue(); 655 if (values.length == 1) 656 { 657 buffer.append(' '); 658 buffer.append(name); 659 buffer.append(" '"); 660 encodeValue(values[0], buffer); 661 buffer.append('\''); 662 } 663 else 664 { 665 buffer.append(' '); 666 buffer.append(name); 667 buffer.append(" ("); 668 for (final String value : values) 669 { 670 buffer.append(" '"); 671 encodeValue(value, buffer); 672 buffer.append('\''); 673 } 674 buffer.append(" )"); 675 } 676 } 677 678 buffer.append(" )"); 679 } 680 681 682 683 /** 684 * Retrieves the OID for this object class. 685 * 686 * @return The OID for this object class. 687 */ 688 public String getOID() 689 { 690 return oid; 691 } 692 693 694 695 /** 696 * Retrieves the set of names for this object class. 697 * 698 * @return The set of names for this object class, or an empty array if it 699 * does not have any names. 700 */ 701 public String[] getNames() 702 { 703 return names; 704 } 705 706 707 708 /** 709 * Retrieves the primary name that can be used to reference this object 710 * class. If one or more names are defined, then the first name will be used. 711 * Otherwise, the OID will be returned. 712 * 713 * @return The primary name that can be used to reference this object class. 714 */ 715 public String getNameOrOID() 716 { 717 if (names.length == 0) 718 { 719 return oid; 720 } 721 else 722 { 723 return names[0]; 724 } 725 } 726 727 728 729 /** 730 * Indicates whether the provided string matches the OID or any of the names 731 * for this object class. 732 * 733 * @param s The string for which to make the determination. It must not be 734 * {@code null}. 735 * 736 * @return {@code true} if the provided string matches the OID or any of the 737 * names for this object class, or {@code false} if not. 738 */ 739 public boolean hasNameOrOID(final String s) 740 { 741 for (final String name : names) 742 { 743 if (s.equalsIgnoreCase(name)) 744 { 745 return true; 746 } 747 } 748 749 return s.equalsIgnoreCase(oid); 750 } 751 752 753 754 /** 755 * Retrieves the description for this object class, if available. 756 * 757 * @return The description for this object class, or {@code null} if there is 758 * no description defined. 759 */ 760 public String getDescription() 761 { 762 return description; 763 } 764 765 766 767 /** 768 * Indicates whether this object class is declared obsolete. 769 * 770 * @return {@code true} if this object class is declared obsolete, or 771 * {@code false} if it is not. 772 */ 773 public boolean isObsolete() 774 { 775 return isObsolete; 776 } 777 778 779 780 /** 781 * Retrieves the names or OIDs of the superior classes for this object class, 782 * if available. 783 * 784 * @return The names or OIDs of the superior classes for this object class, 785 * or an empty array if it does not have any superior classes. 786 */ 787 public String[] getSuperiorClasses() 788 { 789 return superiorClasses; 790 } 791 792 793 794 /** 795 * Retrieves the object class definitions for the superior object classes. 796 * 797 * @param schema The schema to use to retrieve the object class 798 * definitions. 799 * @param recursive Indicates whether to recursively include all of the 800 * superior object class definitions from superior classes. 801 * 802 * @return The object class definitions for the superior object classes. 803 */ 804 public Set<ObjectClassDefinition> getSuperiorClasses(final Schema schema, 805 final boolean recursive) 806 { 807 final LinkedHashSet<ObjectClassDefinition> ocSet = 808 new LinkedHashSet<ObjectClassDefinition>(); 809 for (final String s : superiorClasses) 810 { 811 final ObjectClassDefinition d = schema.getObjectClass(s); 812 if (d != null) 813 { 814 ocSet.add(d); 815 if (recursive) 816 { 817 getSuperiorClasses(schema, d, ocSet); 818 } 819 } 820 } 821 822 return Collections.unmodifiableSet(ocSet); 823 } 824 825 826 827 /** 828 * Recursively adds superior class definitions to the provided set. 829 * 830 * @param schema The schema to use to retrieve the object class definitions. 831 * @param oc The object class definition to be processed. 832 * @param ocSet The set to which the definitions should be added. 833 */ 834 private static void getSuperiorClasses(final Schema schema, 835 final ObjectClassDefinition oc, 836 final Set<ObjectClassDefinition> ocSet) 837 { 838 for (final String s : oc.superiorClasses) 839 { 840 final ObjectClassDefinition d = schema.getObjectClass(s); 841 if (d != null) 842 { 843 ocSet.add(d); 844 getSuperiorClasses(schema, d, ocSet); 845 } 846 } 847 } 848 849 850 851 /** 852 * Retrieves the object class type for this object class. This method will 853 * return {@code null} if this object class definition does not explicitly 854 * specify the object class type, although in that case, the object class type 855 * should be assumed to be {@link ObjectClassType#STRUCTURAL} as per RFC 4512 856 * section 4.1.1. 857 * 858 * @return The object class type for this object class, or {@code null} if it 859 * is not defined in the schema element. 860 */ 861 public ObjectClassType getObjectClassType() 862 { 863 return objectClassType; 864 } 865 866 867 868 /** 869 * Retrieves the object class type for this object class, recursively 870 * examining superior classes if necessary to make the determination. 871 * <BR><BR> 872 * Note that versions of this method before the 4.0.6 release of the LDAP SDK 873 * operated under the incorrect assumption that if an object class definition 874 * did not explicitly specify the object class type, it would be inherited 875 * from its superclass. The correct behavior, as per RFC 4512 section 4.1.1, 876 * is that if the object class type is not explicitly specified, it should be 877 * assumed to be {@link ObjectClassType#STRUCTURAL}. 878 * 879 * @param schema The schema to use to retrieve the definitions for the 880 * superior object classes. As of LDAP SDK version 4.0.6, 881 * this argument is no longer used (and may be {@code null} if 882 * desired), but this version of the method has been preserved 883 * both for the purpose of retaining API compatibility with 884 * previous versions of the LDAP SDK, and to disambiguate it 885 * from the zero-argument version of the method that returns 886 * {@code null} if the object class type is not explicitly 887 * specified. 888 * 889 * @return The object class type for this object class, or 890 * {@link ObjectClassType#STRUCTURAL} if it is not explicitly 891 * defined. 892 */ 893 public ObjectClassType getObjectClassType(final Schema schema) 894 { 895 if (objectClassType == null) 896 { 897 return ObjectClassType.STRUCTURAL; 898 } 899 else 900 { 901 return objectClassType; 902 } 903 } 904 905 906 907 /** 908 * Retrieves the names or OIDs of the attributes that are required to be 909 * present in entries containing this object class. Note that this will not 910 * automatically include the set of required attributes from any superior 911 * classes. 912 * 913 * @return The names or OIDs of the attributes that are required to be 914 * present in entries containing this object class, or an empty array 915 * if there are no required attributes. 916 */ 917 public String[] getRequiredAttributes() 918 { 919 return requiredAttributes; 920 } 921 922 923 924 /** 925 * Retrieves the attribute type definitions for the attributes that are 926 * required to be present in entries containing this object class, optionally 927 * including the set of required attribute types from superior classes. 928 * 929 * @param schema The schema to use to retrieve the 930 * attribute type definitions. 931 * @param includeSuperiorClasses Indicates whether to include definitions 932 * for required attribute types in superior 933 * object classes. 934 * 935 * @return The attribute type definitions for the attributes that are 936 * required to be present in entries containing this object class. 937 */ 938 public Set<AttributeTypeDefinition> getRequiredAttributes(final Schema schema, 939 final boolean includeSuperiorClasses) 940 { 941 final HashSet<AttributeTypeDefinition> attrSet = 942 new HashSet<AttributeTypeDefinition>(); 943 for (final String s : requiredAttributes) 944 { 945 final AttributeTypeDefinition d = schema.getAttributeType(s); 946 if (d != null) 947 { 948 attrSet.add(d); 949 } 950 } 951 952 if (includeSuperiorClasses) 953 { 954 for (final String s : superiorClasses) 955 { 956 final ObjectClassDefinition d = schema.getObjectClass(s); 957 if (d != null) 958 { 959 getSuperiorRequiredAttributes(schema, d, attrSet); 960 } 961 } 962 } 963 964 return Collections.unmodifiableSet(attrSet); 965 } 966 967 968 969 /** 970 * Recursively adds the required attributes from the provided object class 971 * to the given set. 972 * 973 * @param schema The schema to use during processing. 974 * @param oc The object class to be processed. 975 * @param attrSet The set to which the attribute type definitions should be 976 * added. 977 */ 978 private static void getSuperiorRequiredAttributes(final Schema schema, 979 final ObjectClassDefinition oc, 980 final Set<AttributeTypeDefinition> attrSet) 981 { 982 for (final String s : oc.requiredAttributes) 983 { 984 final AttributeTypeDefinition d = schema.getAttributeType(s); 985 if (d != null) 986 { 987 attrSet.add(d); 988 } 989 } 990 991 for (final String s : oc.superiorClasses) 992 { 993 final ObjectClassDefinition d = schema.getObjectClass(s); 994 if (d != null) 995 { 996 getSuperiorRequiredAttributes(schema, d, attrSet); 997 } 998 } 999 } 1000 1001 1002 1003 /** 1004 * Retrieves the names or OIDs of the attributes that may optionally be 1005 * present in entries containing this object class. Note that this will not 1006 * automatically include the set of optional attributes from any superior 1007 * classes. 1008 * 1009 * @return The names or OIDs of the attributes that may optionally be present 1010 * in entries containing this object class, or an empty array if 1011 * there are no optional attributes. 1012 */ 1013 public String[] getOptionalAttributes() 1014 { 1015 return optionalAttributes; 1016 } 1017 1018 1019 1020 /** 1021 * Retrieves the attribute type definitions for the attributes that may 1022 * optionally be present in entries containing this object class, optionally 1023 * including the set of optional attribute types from superior classes. 1024 * 1025 * @param schema The schema to use to retrieve the 1026 * attribute type definitions. 1027 * @param includeSuperiorClasses Indicates whether to include definitions 1028 * for optional attribute types in superior 1029 * object classes. 1030 * 1031 * @return The attribute type definitions for the attributes that may 1032 * optionally be present in entries containing this object class. 1033 */ 1034 public Set<AttributeTypeDefinition> getOptionalAttributes(final Schema schema, 1035 final boolean includeSuperiorClasses) 1036 { 1037 final HashSet<AttributeTypeDefinition> attrSet = 1038 new HashSet<AttributeTypeDefinition>(); 1039 for (final String s : optionalAttributes) 1040 { 1041 final AttributeTypeDefinition d = schema.getAttributeType(s); 1042 if (d != null) 1043 { 1044 attrSet.add(d); 1045 } 1046 } 1047 1048 if (includeSuperiorClasses) 1049 { 1050 final Set<AttributeTypeDefinition> requiredAttrs = 1051 getRequiredAttributes(schema, true); 1052 for (final AttributeTypeDefinition d : requiredAttrs) 1053 { 1054 attrSet.remove(d); 1055 } 1056 1057 for (final String s : superiorClasses) 1058 { 1059 final ObjectClassDefinition d = schema.getObjectClass(s); 1060 if (d != null) 1061 { 1062 getSuperiorOptionalAttributes(schema, d, attrSet, requiredAttrs); 1063 } 1064 } 1065 } 1066 1067 return Collections.unmodifiableSet(attrSet); 1068 } 1069 1070 1071 1072 /** 1073 * Recursively adds the optional attributes from the provided object class 1074 * to the given set. 1075 * 1076 * @param schema The schema to use during processing. 1077 * @param oc The object class to be processed. 1078 * @param attrSet The set to which the attribute type definitions should 1079 * be added. 1080 * @param requiredSet x 1081 */ 1082 private static void getSuperiorOptionalAttributes(final Schema schema, 1083 final ObjectClassDefinition oc, 1084 final Set<AttributeTypeDefinition> attrSet, 1085 final Set<AttributeTypeDefinition> requiredSet) 1086 { 1087 for (final String s : oc.optionalAttributes) 1088 { 1089 final AttributeTypeDefinition d = schema.getAttributeType(s); 1090 if ((d != null) && (! requiredSet.contains(d))) 1091 { 1092 attrSet.add(d); 1093 } 1094 } 1095 1096 for (final String s : oc.superiorClasses) 1097 { 1098 final ObjectClassDefinition d = schema.getObjectClass(s); 1099 if (d != null) 1100 { 1101 getSuperiorOptionalAttributes(schema, d, attrSet, requiredSet); 1102 } 1103 } 1104 } 1105 1106 1107 1108 /** 1109 * Retrieves the set of extensions for this object class. They will be mapped 1110 * from the extension name (which should start with "X-") to the set of values 1111 * for that extension. 1112 * 1113 * @return The set of extensions for this object class. 1114 */ 1115 public Map<String,String[]> getExtensions() 1116 { 1117 return extensions; 1118 } 1119 1120 1121 1122 /** 1123 * {@inheritDoc} 1124 */ 1125 @Override() 1126 public int hashCode() 1127 { 1128 return oid.hashCode(); 1129 } 1130 1131 1132 1133 /** 1134 * {@inheritDoc} 1135 */ 1136 @Override() 1137 public boolean equals(final Object o) 1138 { 1139 if (o == null) 1140 { 1141 return false; 1142 } 1143 1144 if (o == this) 1145 { 1146 return true; 1147 } 1148 1149 if (! (o instanceof ObjectClassDefinition)) 1150 { 1151 return false; 1152 } 1153 1154 final ObjectClassDefinition d = (ObjectClassDefinition) o; 1155 return (oid.equals(d.oid) && 1156 stringsEqualIgnoreCaseOrderIndependent(names, d.names) && 1157 stringsEqualIgnoreCaseOrderIndependent(requiredAttributes, 1158 d.requiredAttributes) && 1159 stringsEqualIgnoreCaseOrderIndependent(optionalAttributes, 1160 d.optionalAttributes) && 1161 stringsEqualIgnoreCaseOrderIndependent(superiorClasses, 1162 d.superiorClasses) && 1163 bothNullOrEqual(objectClassType, d.objectClassType) && 1164 bothNullOrEqualIgnoreCase(description, d.description) && 1165 (isObsolete == d.isObsolete) && 1166 extensionsEqual(extensions, d.extensions)); 1167 } 1168 1169 1170 1171 /** 1172 * Retrieves a string representation of this object class definition, in the 1173 * format described in RFC 4512 section 4.1.1. 1174 * 1175 * @return A string representation of this object class definition. 1176 */ 1177 @Override() 1178 public String toString() 1179 { 1180 return objectClassString; 1181 } 1182}