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