001/* 002 * Copyright 2007-2017 UnboundID Corp. 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2017 UnboundID Corp. 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. 853 * 854 * @return The object class type for this object class, or {@code null} if it 855 * is not defined. 856 */ 857 public ObjectClassType getObjectClassType() 858 { 859 return objectClassType; 860 } 861 862 863 864 /** 865 * Retrieves the object class type for this object class, recursively 866 * examining superior classes if necessary to make the determination. 867 * 868 * @param schema The schema to use to retrieve the definitions for the 869 * superior object classes. 870 * 871 * @return The object class type for this object class. 872 */ 873 public ObjectClassType getObjectClassType(final Schema schema) 874 { 875 if (objectClassType != null) 876 { 877 return objectClassType; 878 } 879 880 for (final String ocName : superiorClasses) 881 { 882 final ObjectClassDefinition d = schema.getObjectClass(ocName); 883 if (d != null) 884 { 885 return d.getObjectClassType(schema); 886 } 887 } 888 889 return ObjectClassType.STRUCTURAL; 890 } 891 892 893 894 /** 895 * Retrieves the names or OIDs of the attributes that are required to be 896 * present in entries containing this object class. Note that this will not 897 * automatically include the set of required attributes from any superior 898 * classes. 899 * 900 * @return The names or OIDs of the attributes that are required to be 901 * present in entries containing this object class, or an empty array 902 * if there are no required attributes. 903 */ 904 public String[] getRequiredAttributes() 905 { 906 return requiredAttributes; 907 } 908 909 910 911 /** 912 * Retrieves the attribute type definitions for the attributes that are 913 * required to be present in entries containing this object class, optionally 914 * including the set of required attribute types from superior classes. 915 * 916 * @param schema The schema to use to retrieve the 917 * attribute type definitions. 918 * @param includeSuperiorClasses Indicates whether to include definitions 919 * for required attribute types in superior 920 * object classes. 921 * 922 * @return The attribute type definitions for the attributes that are 923 * required to be present in entries containing this object class. 924 */ 925 public Set<AttributeTypeDefinition> getRequiredAttributes(final Schema schema, 926 final boolean includeSuperiorClasses) 927 { 928 final HashSet<AttributeTypeDefinition> attrSet = 929 new HashSet<AttributeTypeDefinition>(); 930 for (final String s : requiredAttributes) 931 { 932 final AttributeTypeDefinition d = schema.getAttributeType(s); 933 if (d != null) 934 { 935 attrSet.add(d); 936 } 937 } 938 939 if (includeSuperiorClasses) 940 { 941 for (final String s : superiorClasses) 942 { 943 final ObjectClassDefinition d = schema.getObjectClass(s); 944 if (d != null) 945 { 946 getSuperiorRequiredAttributes(schema, d, attrSet); 947 } 948 } 949 } 950 951 return Collections.unmodifiableSet(attrSet); 952 } 953 954 955 956 /** 957 * Recursively adds the required attributes from the provided object class 958 * to the given set. 959 * 960 * @param schema The schema to use during processing. 961 * @param oc The object class to be processed. 962 * @param attrSet The set to which the attribute type definitions should be 963 * added. 964 */ 965 private static void getSuperiorRequiredAttributes(final Schema schema, 966 final ObjectClassDefinition oc, 967 final Set<AttributeTypeDefinition> attrSet) 968 { 969 for (final String s : oc.requiredAttributes) 970 { 971 final AttributeTypeDefinition d = schema.getAttributeType(s); 972 if (d != null) 973 { 974 attrSet.add(d); 975 } 976 } 977 978 for (final String s : oc.superiorClasses) 979 { 980 final ObjectClassDefinition d = schema.getObjectClass(s); 981 if (d != null) 982 { 983 getSuperiorRequiredAttributes(schema, d, attrSet); 984 } 985 } 986 } 987 988 989 990 /** 991 * Retrieves the names or OIDs of the attributes that may optionally be 992 * present in entries containing this object class. Note that this will not 993 * automatically include the set of optional attributes from any superior 994 * classes. 995 * 996 * @return The names or OIDs of the attributes that may optionally be present 997 * in entries containing this object class, or an empty array if 998 * there are no optional attributes. 999 */ 1000 public String[] getOptionalAttributes() 1001 { 1002 return optionalAttributes; 1003 } 1004 1005 1006 1007 /** 1008 * Retrieves the attribute type definitions for the attributes that may 1009 * optionally be present in entries containing this object class, optionally 1010 * including the set of optional attribute types from superior classes. 1011 * 1012 * @param schema The schema to use to retrieve the 1013 * attribute type definitions. 1014 * @param includeSuperiorClasses Indicates whether to include definitions 1015 * for optional attribute types in superior 1016 * object classes. 1017 * 1018 * @return The attribute type definitions for the attributes that may 1019 * optionally be present in entries containing this object class. 1020 */ 1021 public Set<AttributeTypeDefinition> getOptionalAttributes(final Schema schema, 1022 final boolean includeSuperiorClasses) 1023 { 1024 final HashSet<AttributeTypeDefinition> attrSet = 1025 new HashSet<AttributeTypeDefinition>(); 1026 for (final String s : optionalAttributes) 1027 { 1028 final AttributeTypeDefinition d = schema.getAttributeType(s); 1029 if (d != null) 1030 { 1031 attrSet.add(d); 1032 } 1033 } 1034 1035 if (includeSuperiorClasses) 1036 { 1037 final Set<AttributeTypeDefinition> requiredAttrs = 1038 getRequiredAttributes(schema, true); 1039 for (final AttributeTypeDefinition d : requiredAttrs) 1040 { 1041 attrSet.remove(d); 1042 } 1043 1044 for (final String s : superiorClasses) 1045 { 1046 final ObjectClassDefinition d = schema.getObjectClass(s); 1047 if (d != null) 1048 { 1049 getSuperiorOptionalAttributes(schema, d, attrSet, requiredAttrs); 1050 } 1051 } 1052 } 1053 1054 return Collections.unmodifiableSet(attrSet); 1055 } 1056 1057 1058 1059 /** 1060 * Recursively adds the optional attributes from the provided object class 1061 * to the given set. 1062 * 1063 * @param schema The schema to use during processing. 1064 * @param oc The object class to be processed. 1065 * @param attrSet The set to which the attribute type definitions should 1066 * be added. 1067 * @param requiredSet x 1068 */ 1069 private static void getSuperiorOptionalAttributes(final Schema schema, 1070 final ObjectClassDefinition oc, 1071 final Set<AttributeTypeDefinition> attrSet, 1072 final Set<AttributeTypeDefinition> requiredSet) 1073 { 1074 for (final String s : oc.optionalAttributes) 1075 { 1076 final AttributeTypeDefinition d = schema.getAttributeType(s); 1077 if ((d != null) && (! requiredSet.contains(d))) 1078 { 1079 attrSet.add(d); 1080 } 1081 } 1082 1083 for (final String s : oc.superiorClasses) 1084 { 1085 final ObjectClassDefinition d = schema.getObjectClass(s); 1086 if (d != null) 1087 { 1088 getSuperiorOptionalAttributes(schema, d, attrSet, requiredSet); 1089 } 1090 } 1091 } 1092 1093 1094 1095 /** 1096 * Retrieves the set of extensions for this object class. They will be mapped 1097 * from the extension name (which should start with "X-") to the set of values 1098 * for that extension. 1099 * 1100 * @return The set of extensions for this object class. 1101 */ 1102 public Map<String,String[]> getExtensions() 1103 { 1104 return extensions; 1105 } 1106 1107 1108 1109 /** 1110 * {@inheritDoc} 1111 */ 1112 @Override() 1113 public int hashCode() 1114 { 1115 return oid.hashCode(); 1116 } 1117 1118 1119 1120 /** 1121 * {@inheritDoc} 1122 */ 1123 @Override() 1124 public boolean equals(final Object o) 1125 { 1126 if (o == null) 1127 { 1128 return false; 1129 } 1130 1131 if (o == this) 1132 { 1133 return true; 1134 } 1135 1136 if (! (o instanceof ObjectClassDefinition)) 1137 { 1138 return false; 1139 } 1140 1141 final ObjectClassDefinition d = (ObjectClassDefinition) o; 1142 return (oid.equals(d.oid) && 1143 stringsEqualIgnoreCaseOrderIndependent(names, d.names) && 1144 stringsEqualIgnoreCaseOrderIndependent(requiredAttributes, 1145 d.requiredAttributes) && 1146 stringsEqualIgnoreCaseOrderIndependent(optionalAttributes, 1147 d.optionalAttributes) && 1148 stringsEqualIgnoreCaseOrderIndependent(superiorClasses, 1149 d.superiorClasses) && 1150 bothNullOrEqual(objectClassType, d.objectClassType) && 1151 bothNullOrEqualIgnoreCase(description, d.description) && 1152 (isObsolete == d.isObsolete) && 1153 extensionsEqual(extensions, d.extensions)); 1154 } 1155 1156 1157 1158 /** 1159 * Retrieves a string representation of this object class definition, in the 1160 * format described in RFC 4512 section 4.1.1. 1161 * 1162 * @return A string representation of this object class definition. 1163 */ 1164 @Override() 1165 public String toString() 1166 { 1167 return objectClassString; 1168 } 1169}