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.io.File; 026import java.io.IOException; 027import java.io.InputStream; 028import java.io.Serializable; 029import java.util.ArrayList; 030import java.util.Arrays; 031import java.util.Collections; 032import java.util.LinkedHashMap; 033import java.util.LinkedHashSet; 034import java.util.List; 035import java.util.Map; 036import java.util.Set; 037import java.util.concurrent.atomic.AtomicReference; 038 039import com.unboundid.ldap.sdk.Attribute; 040import com.unboundid.ldap.sdk.Entry; 041import com.unboundid.ldap.sdk.Filter; 042import com.unboundid.ldap.sdk.LDAPConnection; 043import com.unboundid.ldap.sdk.LDAPException; 044import com.unboundid.ldap.sdk.ReadOnlyEntry; 045import com.unboundid.ldap.sdk.ResultCode; 046import com.unboundid.ldap.sdk.SearchScope; 047import com.unboundid.ldif.LDIFException; 048import com.unboundid.ldif.LDIFReader; 049import com.unboundid.util.Debug; 050import com.unboundid.util.NotMutable; 051import com.unboundid.util.StaticUtils; 052import com.unboundid.util.ThreadSafety; 053import com.unboundid.util.ThreadSafetyLevel; 054import com.unboundid.util.Validator; 055 056import static com.unboundid.ldap.sdk.schema.SchemaMessages.*; 057 058 059 060/** 061 * This class provides a data structure for representing a directory server 062 * subschema subentry. This includes information about the attribute syntaxes, 063 * matching rules, attribute types, object classes, name forms, DIT content 064 * rules, DIT structure rules, and matching rule uses defined in the server 065 * schema. 066 */ 067@NotMutable() 068@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE) 069public final class Schema 070 implements Serializable 071{ 072 /** 073 * The name of the attribute used to hold the attribute syntax definitions. 074 */ 075 public static final String ATTR_ATTRIBUTE_SYNTAX = "ldapSyntaxes"; 076 077 078 079 /** 080 * The name of the attribute used to hold the attribute type definitions. 081 */ 082 public static final String ATTR_ATTRIBUTE_TYPE = "attributeTypes"; 083 084 085 086 /** 087 * The name of the attribute used to hold the DIT content rule definitions. 088 */ 089 public static final String ATTR_DIT_CONTENT_RULE = "dITContentRules"; 090 091 092 093 /** 094 * The name of the attribute used to hold the DIT structure rule definitions. 095 */ 096 public static final String ATTR_DIT_STRUCTURE_RULE = "dITStructureRules"; 097 098 099 100 /** 101 * The name of the attribute used to hold the matching rule definitions. 102 */ 103 public static final String ATTR_MATCHING_RULE = "matchingRules"; 104 105 106 107 /** 108 * The name of the attribute used to hold the matching rule use definitions. 109 */ 110 public static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse"; 111 112 113 114 /** 115 * The name of the attribute used to hold the name form definitions. 116 */ 117 public static final String ATTR_NAME_FORM = "nameForms"; 118 119 120 121 /** 122 * The name of the attribute used to hold the object class definitions. 123 */ 124 public static final String ATTR_OBJECT_CLASS = "objectClasses"; 125 126 127 128 /** 129 * The name of the attribute used to hold the DN of the subschema subentry 130 * with the schema information that governs a specified entry. 131 */ 132 public static final String ATTR_SUBSCHEMA_SUBENTRY = "subschemaSubentry"; 133 134 135 136 /** 137 * The default standard schema available for use in the LDAP SDK. 138 */ 139 private static final AtomicReference<Schema> DEFAULT_STANDARD_SCHEMA = 140 new AtomicReference<>(); 141 142 143 144 /** 145 * The set of request attributes that will be used when retrieving the server 146 * subschema subentry in order to retrieve all of the schema elements. 147 */ 148 private static final String[] SCHEMA_REQUEST_ATTRS = 149 { 150 "*", 151 ATTR_ATTRIBUTE_SYNTAX, 152 ATTR_ATTRIBUTE_TYPE, 153 ATTR_DIT_CONTENT_RULE, 154 ATTR_DIT_STRUCTURE_RULE, 155 ATTR_MATCHING_RULE, 156 ATTR_MATCHING_RULE_USE, 157 ATTR_NAME_FORM, 158 ATTR_OBJECT_CLASS 159 }; 160 161 162 163 /** 164 * The set of request attributes that will be used when retrieving the 165 * subschema subentry attribute from a specified entry in order to determine 166 * the location of the server schema definitions. 167 */ 168 private static final String[] SUBSCHEMA_SUBENTRY_REQUEST_ATTRS = 169 { 170 ATTR_SUBSCHEMA_SUBENTRY 171 }; 172 173 174 175 /** 176 * Retrieves the resource path that may be used to obtain a file with a number 177 * of standard schema definitions. 178 */ 179 private static final String DEFAULT_SCHEMA_RESOURCE_PATH = 180 "com/unboundid/ldap/sdk/schema/standard-schema.ldif"; 181 182 183 184 /** 185 * The serial version UID for this serializable class. 186 */ 187 private static final long serialVersionUID = 8081839633831517925L; 188 189 190 191 // A map of all subordinate attribute type definitions for each attribute 192 // type definition. 193 private final Map<AttributeTypeDefinition,List<AttributeTypeDefinition>> 194 subordinateAttributeTypes; 195 196 // The set of attribute syntaxes mapped from lowercase name/OID to syntax. 197 private final Map<String,AttributeSyntaxDefinition> asMap; 198 199 // The set of attribute types mapped from lowercase name/OID to type. 200 private final Map<String,AttributeTypeDefinition> atMap; 201 202 // The set of DIT content rules mapped from lowercase name/OID to rule. 203 private final Map<String,DITContentRuleDefinition> dcrMap; 204 205 // The set of DIT structure rules mapped from rule ID to rule. 206 private final Map<Integer,DITStructureRuleDefinition> dsrMapByID; 207 208 // The set of DIT structure rules mapped from lowercase name to rule. 209 private final Map<String,DITStructureRuleDefinition> dsrMapByName; 210 211 // The set of DIT structure rules mapped from lowercase name to rule. 212 private final Map<String,DITStructureRuleDefinition> dsrMapByNameForm; 213 214 // The set of matching rules mapped from lowercase name/OID to rule. 215 private final Map<String,MatchingRuleDefinition> mrMap; 216 217 // The set of matching rule uses mapped from matching rule OID to use. 218 private final Map<String,MatchingRuleUseDefinition> mruMap; 219 220 // The set of name forms mapped from lowercase name/OID to name form. 221 private final Map<String,NameFormDefinition> nfMapByName; 222 223 // The set of name forms mapped from structural class OID to name form. 224 private final Map<String,NameFormDefinition> nfMapByOC; 225 226 // The set of object classes mapped from lowercase name/OID to class. 227 private final Map<String,ObjectClassDefinition> ocMap; 228 229 // The entry used to create this schema object. 230 private final ReadOnlyEntry schemaEntry; 231 232 // The set of attribute syntaxes defined in the schema. 233 private final Set<AttributeSyntaxDefinition> asSet; 234 235 // The set of attribute types defined in the schema. 236 private final Set<AttributeTypeDefinition> atSet; 237 238 // The set of operational attribute types defined in the schema. 239 private final Set<AttributeTypeDefinition> operationalATSet; 240 241 // The set of user attribute types defined in the schema. 242 private final Set<AttributeTypeDefinition> userATSet; 243 244 // The set of DIT content rules defined in the schema. 245 private final Set<DITContentRuleDefinition> dcrSet; 246 247 // The set of DIT structure rules defined in the schema. 248 private final Set<DITStructureRuleDefinition> dsrSet; 249 250 // The set of matching rules defined in the schema. 251 private final Set<MatchingRuleDefinition> mrSet; 252 253 // The set of matching rule uses defined in the schema. 254 private final Set<MatchingRuleUseDefinition> mruSet; 255 256 // The set of name forms defined in the schema. 257 private final Set<NameFormDefinition> nfSet; 258 259 // The set of object classes defined in the schema. 260 private final Set<ObjectClassDefinition> ocSet; 261 262 // The set of abstract object classes defined in the schema. 263 private final Set<ObjectClassDefinition> abstractOCSet; 264 265 // The set of auxiliary object classes defined in the schema. 266 private final Set<ObjectClassDefinition> auxiliaryOCSet; 267 268 // The set of structural object classes defined in the schema. 269 private final Set<ObjectClassDefinition> structuralOCSet; 270 271 272 273 /** 274 * Creates a new schema object by decoding the information in the provided 275 * entry. Any schema elements that cannot be parsed will be silently ignored. 276 * 277 * @param schemaEntry The schema entry to decode. It must not be 278 * {@code null}. 279 */ 280 public Schema(final Entry schemaEntry) 281 { 282 this(schemaEntry, null, null, null, null, null, null, null, null); 283 } 284 285 286 287 /** 288 * Creates a new schema object by decoding the information in the provided 289 * entry, optionally capturing any information about unparsable values in the 290 * provided maps. 291 * 292 * @param schemaEntry The schema entry to decode. It must 293 * not be {@code null}. 294 * @param unparsableAttributeSyntaxes A map that will be updated with with 295 * information about any attribute syntax 296 * definitions that cannot be parsed. It 297 * may be {@code null} if unparsable 298 * attribute syntax definitions should be 299 * silently ignored. 300 * @param unparsableMatchingRules A map that will be updated with with 301 * information about any matching rule 302 * definitions that cannot be parsed. It 303 * may be {@code null} if unparsable 304 * attribute syntax definitions should be 305 * silently ignored. 306 * @param unparsableAttributeTypes A map that will be updated with with 307 * information about any attribute type 308 * definitions that cannot be parsed. It 309 * may be {@code null} if unparsable 310 * attribute syntax definitions should be 311 * silently ignored. 312 * @param unparsableObjectClasses A map that will be updated with with 313 * information about any object class 314 * definitions that cannot be parsed. It 315 * may be {@code null} if unparsable 316 * attribute syntax definitions should be 317 * silently ignored. 318 * @param unparsableDITContentRules A map that will be updated with with 319 * information about any DIT content rule 320 * definitions that cannot be parsed. It 321 * may be {@code null} if unparsable 322 * attribute syntax definitions should be 323 * silently ignored. 324 * @param unparsableDITStructureRules A map that will be updated with with 325 * information about any DIT structure 326 * rule definitions that cannot be 327 * parsed. It may be {@code null} if 328 * unparsable attribute syntax 329 * definitions should be silently 330 * ignored. 331 * @param unparsableNameForms A map that will be updated with with 332 * information about any name form 333 * definitions that cannot be parsed. It 334 * may be {@code null} if unparsable 335 * attribute syntax definitions should be 336 * silently ignored. 337 * @param unparsableMatchingRuleUses A map that will be updated with with 338 * information about any matching rule 339 * use definitions that cannot be parsed. 340 * It may be {@code null} if unparsable 341 * attribute syntax definitions should be 342 * silently ignored. 343 */ 344 public Schema(final Entry schemaEntry, 345 final Map<String,LDAPException> unparsableAttributeSyntaxes, 346 final Map<String,LDAPException> unparsableMatchingRules, 347 final Map<String,LDAPException> unparsableAttributeTypes, 348 final Map<String,LDAPException> unparsableObjectClasses, 349 final Map<String,LDAPException> unparsableDITContentRules, 350 final Map<String,LDAPException> unparsableDITStructureRules, 351 final Map<String,LDAPException> unparsableNameForms, 352 final Map<String,LDAPException> unparsableMatchingRuleUses) 353 { 354 this.schemaEntry = new ReadOnlyEntry(schemaEntry); 355 356 // Decode the attribute syntaxes from the schema entry. 357 String[] defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_SYNTAX); 358 if (defs == null) 359 { 360 asMap = Collections.emptyMap(); 361 asSet = Collections.emptySet(); 362 } 363 else 364 { 365 final LinkedHashMap<String,AttributeSyntaxDefinition> m = 366 new LinkedHashMap<>(defs.length); 367 final LinkedHashSet<AttributeSyntaxDefinition> s = 368 new LinkedHashSet<>(defs.length); 369 370 for (final String def : defs) 371 { 372 try 373 { 374 final AttributeSyntaxDefinition as = 375 new AttributeSyntaxDefinition(def); 376 s.add(as); 377 m.put(StaticUtils.toLowerCase(as.getOID()), as); 378 } 379 catch (final LDAPException le) 380 { 381 Debug.debugException(le); 382 if (unparsableAttributeSyntaxes != null) 383 { 384 unparsableAttributeSyntaxes.put(def, le); 385 } 386 } 387 } 388 389 asMap = Collections.unmodifiableMap(m); 390 asSet = Collections.unmodifiableSet(s); 391 } 392 393 394 // Decode the attribute types from the schema entry. 395 defs = schemaEntry.getAttributeValues(ATTR_ATTRIBUTE_TYPE); 396 if (defs == null) 397 { 398 atMap = Collections.emptyMap(); 399 atSet = Collections.emptySet(); 400 operationalATSet = Collections.emptySet(); 401 userATSet = Collections.emptySet(); 402 } 403 else 404 { 405 final LinkedHashMap<String,AttributeTypeDefinition> m = 406 new LinkedHashMap<>(2*defs.length); 407 final LinkedHashSet<AttributeTypeDefinition> s = 408 new LinkedHashSet<>(defs.length); 409 final LinkedHashSet<AttributeTypeDefinition> sUser = 410 new LinkedHashSet<>(defs.length); 411 final LinkedHashSet<AttributeTypeDefinition> sOperational = 412 new LinkedHashSet<>(defs.length); 413 414 for (final String def : defs) 415 { 416 try 417 { 418 final AttributeTypeDefinition at = new AttributeTypeDefinition(def); 419 s.add(at); 420 m.put(StaticUtils.toLowerCase(at.getOID()), at); 421 for (final String name : at.getNames()) 422 { 423 m.put(StaticUtils.toLowerCase(name), at); 424 } 425 426 if (at.isOperational()) 427 { 428 sOperational.add(at); 429 } 430 else 431 { 432 sUser.add(at); 433 } 434 } 435 catch (final LDAPException le) 436 { 437 Debug.debugException(le); 438 if (unparsableAttributeTypes != null) 439 { 440 unparsableAttributeTypes.put(def, le); 441 } 442 } 443 } 444 445 atMap = Collections.unmodifiableMap(m); 446 atSet = Collections.unmodifiableSet(s); 447 operationalATSet = Collections.unmodifiableSet(sOperational); 448 userATSet = Collections.unmodifiableSet(sUser); 449 } 450 451 452 // Decode the DIT content rules from the schema entry. 453 defs = schemaEntry.getAttributeValues(ATTR_DIT_CONTENT_RULE); 454 if (defs == null) 455 { 456 dcrMap = Collections.emptyMap(); 457 dcrSet = Collections.emptySet(); 458 } 459 else 460 { 461 final LinkedHashMap<String,DITContentRuleDefinition> m = 462 new LinkedHashMap<>(2*defs.length); 463 final LinkedHashSet<DITContentRuleDefinition> s = 464 new LinkedHashSet<>(defs.length); 465 466 for (final String def : defs) 467 { 468 try 469 { 470 final DITContentRuleDefinition dcr = 471 new DITContentRuleDefinition(def); 472 s.add(dcr); 473 m.put(StaticUtils.toLowerCase(dcr.getOID()), dcr); 474 for (final String name : dcr.getNames()) 475 { 476 m.put(StaticUtils.toLowerCase(name), dcr); 477 } 478 } 479 catch (final LDAPException le) 480 { 481 Debug.debugException(le); 482 if (unparsableDITContentRules != null) 483 { 484 unparsableDITContentRules.put(def, le); 485 } 486 } 487 } 488 489 dcrMap = Collections.unmodifiableMap(m); 490 dcrSet = Collections.unmodifiableSet(s); 491 } 492 493 494 // Decode the DIT structure rules from the schema entry. 495 defs = schemaEntry.getAttributeValues(ATTR_DIT_STRUCTURE_RULE); 496 if (defs == null) 497 { 498 dsrMapByID = Collections.emptyMap(); 499 dsrMapByName = Collections.emptyMap(); 500 dsrMapByNameForm = Collections.emptyMap(); 501 dsrSet = Collections.emptySet(); 502 } 503 else 504 { 505 final LinkedHashMap<Integer,DITStructureRuleDefinition> mID = 506 new LinkedHashMap<>(defs.length); 507 final LinkedHashMap<String,DITStructureRuleDefinition> mN = 508 new LinkedHashMap<>(defs.length); 509 final LinkedHashMap<String,DITStructureRuleDefinition> mNF = 510 new LinkedHashMap<>(defs.length); 511 final LinkedHashSet<DITStructureRuleDefinition> s = 512 new LinkedHashSet<>(defs.length); 513 514 for (final String def : defs) 515 { 516 try 517 { 518 final DITStructureRuleDefinition dsr = 519 new DITStructureRuleDefinition(def); 520 s.add(dsr); 521 mID.put(dsr.getRuleID(), dsr); 522 mNF.put(StaticUtils.toLowerCase(dsr.getNameFormID()), dsr); 523 for (final String name : dsr.getNames()) 524 { 525 mN.put(StaticUtils.toLowerCase(name), dsr); 526 } 527 } 528 catch (final LDAPException le) 529 { 530 Debug.debugException(le); 531 if (unparsableDITStructureRules != null) 532 { 533 unparsableDITStructureRules.put(def, le); 534 } 535 } 536 } 537 538 dsrMapByID = Collections.unmodifiableMap(mID); 539 dsrMapByName = Collections.unmodifiableMap(mN); 540 dsrMapByNameForm = Collections.unmodifiableMap(mNF); 541 dsrSet = Collections.unmodifiableSet(s); 542 } 543 544 545 // Decode the matching rules from the schema entry. 546 defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE); 547 if (defs == null) 548 { 549 mrMap = Collections.emptyMap(); 550 mrSet = Collections.emptySet(); 551 } 552 else 553 { 554 final LinkedHashMap<String,MatchingRuleDefinition> m = 555 new LinkedHashMap<>(2*defs.length); 556 final LinkedHashSet<MatchingRuleDefinition> s = 557 new LinkedHashSet<>(defs.length); 558 559 for (final String def : defs) 560 { 561 try 562 { 563 final MatchingRuleDefinition mr = new MatchingRuleDefinition(def); 564 s.add(mr); 565 m.put(StaticUtils.toLowerCase(mr.getOID()), mr); 566 for (final String name : mr.getNames()) 567 { 568 m.put(StaticUtils.toLowerCase(name), mr); 569 } 570 } 571 catch (final LDAPException le) 572 { 573 Debug.debugException(le); 574 if (unparsableMatchingRules != null) 575 { 576 unparsableMatchingRules.put(def, le); 577 } 578 } 579 } 580 581 mrMap = Collections.unmodifiableMap(m); 582 mrSet = Collections.unmodifiableSet(s); 583 } 584 585 586 // Decode the matching rule uses from the schema entry. 587 defs = schemaEntry.getAttributeValues(ATTR_MATCHING_RULE_USE); 588 if (defs == null) 589 { 590 mruMap = Collections.emptyMap(); 591 mruSet = Collections.emptySet(); 592 } 593 else 594 { 595 final LinkedHashMap<String,MatchingRuleUseDefinition> m = 596 new LinkedHashMap<>(2*defs.length); 597 final LinkedHashSet<MatchingRuleUseDefinition> s = 598 new LinkedHashSet<>(defs.length); 599 600 for (final String def : defs) 601 { 602 try 603 { 604 final MatchingRuleUseDefinition mru = 605 new MatchingRuleUseDefinition(def); 606 s.add(mru); 607 m.put(StaticUtils.toLowerCase(mru.getOID()), mru); 608 for (final String name : mru.getNames()) 609 { 610 m.put(StaticUtils.toLowerCase(name), mru); 611 } 612 } 613 catch (final LDAPException le) 614 { 615 Debug.debugException(le); 616 if (unparsableMatchingRuleUses != null) 617 { 618 unparsableMatchingRuleUses.put(def, le); 619 } 620 } 621 } 622 623 mruMap = Collections.unmodifiableMap(m); 624 mruSet = Collections.unmodifiableSet(s); 625 } 626 627 628 // Decode the name forms from the schema entry. 629 defs = schemaEntry.getAttributeValues(ATTR_NAME_FORM); 630 if (defs == null) 631 { 632 nfMapByName = Collections.emptyMap(); 633 nfMapByOC = Collections.emptyMap(); 634 nfSet = Collections.emptySet(); 635 } 636 else 637 { 638 final LinkedHashMap<String,NameFormDefinition> mN = 639 new LinkedHashMap<>(2*defs.length); 640 final LinkedHashMap<String,NameFormDefinition> mOC = 641 new LinkedHashMap<>(defs.length); 642 final LinkedHashSet<NameFormDefinition> s = 643 new LinkedHashSet<>(defs.length); 644 645 for (final String def : defs) 646 { 647 try 648 { 649 final NameFormDefinition nf = new NameFormDefinition(def); 650 s.add(nf); 651 mOC.put(StaticUtils.toLowerCase(nf.getStructuralClass()), nf); 652 mN.put(StaticUtils.toLowerCase(nf.getOID()), nf); 653 for (final String name : nf.getNames()) 654 { 655 mN.put(StaticUtils.toLowerCase(name), nf); 656 } 657 } 658 catch (final LDAPException le) 659 { 660 Debug.debugException(le); 661 if(unparsableNameForms != null) 662 { 663 unparsableNameForms.put(def, le); 664 } 665 } 666 } 667 668 nfMapByName = Collections.unmodifiableMap(mN); 669 nfMapByOC = Collections.unmodifiableMap(mOC); 670 nfSet = Collections.unmodifiableSet(s); 671 } 672 673 674 // Decode the object classes from the schema entry. 675 defs = schemaEntry.getAttributeValues(ATTR_OBJECT_CLASS); 676 if (defs == null) 677 { 678 ocMap = Collections.emptyMap(); 679 ocSet = Collections.emptySet(); 680 abstractOCSet = Collections.emptySet(); 681 auxiliaryOCSet = Collections.emptySet(); 682 structuralOCSet = Collections.emptySet(); 683 } 684 else 685 { 686 final LinkedHashMap<String,ObjectClassDefinition> m = 687 new LinkedHashMap<>(2*defs.length); 688 final LinkedHashSet<ObjectClassDefinition> s = 689 new LinkedHashSet<>(defs.length); 690 final LinkedHashSet<ObjectClassDefinition> sAbstract = 691 new LinkedHashSet<>(defs.length); 692 final LinkedHashSet<ObjectClassDefinition> sAuxiliary = 693 new LinkedHashSet<>(defs.length); 694 final LinkedHashSet<ObjectClassDefinition> sStructural = 695 new LinkedHashSet<>(defs.length); 696 697 for (final String def : defs) 698 { 699 try 700 { 701 final ObjectClassDefinition oc = new ObjectClassDefinition(def); 702 s.add(oc); 703 m.put(StaticUtils.toLowerCase(oc.getOID()), oc); 704 for (final String name : oc.getNames()) 705 { 706 m.put(StaticUtils.toLowerCase(name), oc); 707 } 708 709 switch (oc.getObjectClassType(null)) 710 { 711 case ABSTRACT: 712 sAbstract.add(oc); 713 break; 714 case AUXILIARY: 715 sAuxiliary.add(oc); 716 break; 717 case STRUCTURAL: 718 sStructural.add(oc); 719 break; 720 } 721 } 722 catch (final LDAPException le) 723 { 724 Debug.debugException(le); 725 if (unparsableObjectClasses != null) 726 { 727 unparsableObjectClasses.put(def, le); 728 } 729 } 730 } 731 732 ocMap = Collections.unmodifiableMap(m); 733 ocSet = Collections.unmodifiableSet(s); 734 abstractOCSet = Collections.unmodifiableSet(sAbstract); 735 auxiliaryOCSet = Collections.unmodifiableSet(sAuxiliary); 736 structuralOCSet = Collections.unmodifiableSet(sStructural); 737 } 738 739 740 // Populate the map of subordinate attribute types. 741 final LinkedHashMap<AttributeTypeDefinition,List<AttributeTypeDefinition>> 742 subAttrTypes = new LinkedHashMap<>(atSet.size()); 743 for (final AttributeTypeDefinition d : atSet) 744 { 745 AttributeTypeDefinition sup = d.getSuperiorType(this); 746 while (sup != null) 747 { 748 List<AttributeTypeDefinition> l = subAttrTypes.get(sup); 749 if (l == null) 750 { 751 l = new ArrayList<>(1); 752 subAttrTypes.put(sup, l); 753 } 754 l.add(d); 755 756 sup = sup.getSuperiorType(this); 757 } 758 } 759 subordinateAttributeTypes = Collections.unmodifiableMap(subAttrTypes); 760 } 761 762 763 764 /** 765 * Parses all schema elements contained in the provided entry. This method 766 * differs from the {@link #Schema(Entry)} constructor in that this method 767 * will throw an exception if it encounters any unparsable schema elements, 768 * while the constructor will silently ignore them. Alternately, the 769 * 'constructor that takes a bunch of maps can be used to 770 * 771 * @param schemaEntry The schema entry to parse. It must not be 772 * {@code null}. 773 * 774 * @return The schema entry that was parsed. 775 * 776 * @throws LDAPException If the provided entry contains any schema element 777 * definitions that cannot be parsed. 778 */ 779 public static Schema parseSchemaEntry(final Entry schemaEntry) 780 throws LDAPException 781 { 782 final Map<String,LDAPException> unparsableAttributeSyntaxes = 783 new LinkedHashMap<>(10); 784 final Map<String,LDAPException> unparsableMatchingRules = 785 new LinkedHashMap<>(10); 786 final Map<String,LDAPException> unparsableAttributeTypes = 787 new LinkedHashMap<>(10); 788 final Map<String,LDAPException> unparsableObjectClasses = 789 new LinkedHashMap<>(10); 790 final Map<String,LDAPException> unparsableDITContentRules = 791 new LinkedHashMap<>(10); 792 final Map<String,LDAPException> unparsableDITStructureRules = 793 new LinkedHashMap<>(10); 794 final Map<String,LDAPException> unparsableNameForms = 795 new LinkedHashMap<>(10); 796 final Map<String,LDAPException> unparsableMatchingRuleUses = 797 new LinkedHashMap<>(10); 798 799 final Schema schema = new Schema(schemaEntry, unparsableAttributeSyntaxes, 800 unparsableMatchingRules, unparsableAttributeTypes, 801 unparsableObjectClasses, unparsableDITContentRules, 802 unparsableDITStructureRules, unparsableNameForms, 803 unparsableMatchingRuleUses); 804 if (unparsableAttributeSyntaxes.isEmpty() && 805 unparsableMatchingRules.isEmpty() && 806 unparsableAttributeTypes.isEmpty() && 807 unparsableObjectClasses.isEmpty() && 808 unparsableDITContentRules.isEmpty() && 809 unparsableDITStructureRules.isEmpty() && 810 unparsableNameForms.isEmpty() && 811 unparsableMatchingRuleUses.isEmpty()) 812 { 813 return schema; 814 } 815 816 final StringBuilder messageBuffer = new StringBuilder(); 817 for (final Map.Entry<String,LDAPException> e : 818 unparsableAttributeSyntaxes.entrySet()) 819 { 820 appendErrorMessage(messageBuffer, 821 ERR_SCHEMA_UNPARSABLE_AS.get(ATTR_ATTRIBUTE_SYNTAX, e.getKey(), 822 StaticUtils.getExceptionMessage(e.getValue()))); 823 } 824 825 for (final Map.Entry<String,LDAPException> e : 826 unparsableMatchingRules.entrySet()) 827 { 828 appendErrorMessage(messageBuffer, 829 ERR_SCHEMA_UNPARSABLE_MR.get(ATTR_MATCHING_RULE, e.getKey(), 830 StaticUtils.getExceptionMessage(e.getValue()))); 831 } 832 833 for (final Map.Entry<String,LDAPException> e : 834 unparsableAttributeTypes.entrySet()) 835 { 836 appendErrorMessage(messageBuffer, 837 ERR_SCHEMA_UNPARSABLE_AT.get(ATTR_ATTRIBUTE_TYPE, e.getKey(), 838 StaticUtils.getExceptionMessage(e.getValue()))); 839 } 840 841 for (final Map.Entry<String,LDAPException> e : 842 unparsableObjectClasses.entrySet()) 843 { 844 appendErrorMessage(messageBuffer, 845 ERR_SCHEMA_UNPARSABLE_OC.get(ATTR_OBJECT_CLASS, e.getKey(), 846 StaticUtils.getExceptionMessage(e.getValue()))); 847 } 848 849 for (final Map.Entry<String,LDAPException> e : 850 unparsableDITContentRules.entrySet()) 851 { 852 appendErrorMessage(messageBuffer, 853 ERR_SCHEMA_UNPARSABLE_DCR.get(ATTR_DIT_CONTENT_RULE, e.getKey(), 854 StaticUtils.getExceptionMessage(e.getValue()))); 855 } 856 857 for (final Map.Entry<String,LDAPException> e : 858 unparsableDITStructureRules.entrySet()) 859 { 860 appendErrorMessage(messageBuffer, 861 ERR_SCHEMA_UNPARSABLE_DSR.get(ATTR_DIT_STRUCTURE_RULE, e.getKey(), 862 StaticUtils.getExceptionMessage(e.getValue()))); 863 } 864 865 for (final Map.Entry<String,LDAPException> e : 866 unparsableNameForms.entrySet()) 867 { 868 appendErrorMessage(messageBuffer, 869 ERR_SCHEMA_UNPARSABLE_NF.get(ATTR_NAME_FORM, e.getKey(), 870 StaticUtils.getExceptionMessage(e.getValue()))); 871 } 872 873 for (final Map.Entry<String,LDAPException> e : 874 unparsableMatchingRuleUses.entrySet()) 875 { 876 appendErrorMessage(messageBuffer, 877 ERR_SCHEMA_UNPARSABLE_MRU.get(ATTR_MATCHING_RULE_USE, e.getKey(), 878 StaticUtils.getExceptionMessage(e.getValue()))); 879 } 880 881 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 882 messageBuffer.toString()); 883 } 884 885 886 887 /** 888 * Appends the provided message to the given buffer, adding spaces and 889 * punctuation if necessary. 890 * 891 * @param buffer The buffer to which the message should be appended. 892 * @param message The message to append to the buffer. 893 */ 894 private static void appendErrorMessage(final StringBuilder buffer, 895 final String message) 896 { 897 final int length = buffer.length(); 898 if (length > 0) 899 { 900 if (buffer.charAt(length - 1) == '.') 901 { 902 buffer.append(" "); 903 } 904 else 905 { 906 buffer.append(". "); 907 } 908 } 909 910 buffer.append(message); 911 } 912 913 914 915 /** 916 * Retrieves the directory server schema over the provided connection. The 917 * root DSE will first be retrieved in order to get its subschemaSubentry DN, 918 * and then that entry will be retrieved from the server and its contents 919 * decoded as schema elements. This should be sufficient for directories that 920 * only provide a single schema, but for directories with multiple schemas it 921 * may be necessary to specify the DN of an entry for which to retrieve the 922 * subschema subentry. Any unparsable schema elements will be silently 923 * ignored. 924 * 925 * @param connection The connection to use in order to retrieve the server 926 * schema. It must not be {@code null}. 927 * 928 * @return A decoded representation of the server schema. 929 * 930 * @throws LDAPException If a problem occurs while obtaining the server 931 * schema. 932 */ 933 public static Schema getSchema(final LDAPConnection connection) 934 throws LDAPException 935 { 936 return getSchema(connection, ""); 937 } 938 939 940 941 /** 942 * Retrieves the directory server schema that governs the specified entry. 943 * In some servers, different portions of the DIT may be served by different 944 * schemas, and in such cases it will be necessary to provide the DN of the 945 * target entry in order to ensure that the appropriate schema which governs 946 * that entry is returned. For servers that support only a single schema, 947 * any entry DN (including that of the root DSE) should be sufficient. Any 948 * unparsable schema elements will be silently ignored. 949 * 950 * @param connection The connection to use in order to retrieve the server 951 * schema. It must not be {@code null}. 952 * @param entryDN The DN of the entry for which to retrieve the governing 953 * schema. It may be {@code null} or an empty string in 954 * order to retrieve the schema that governs the server's 955 * root DSE. 956 * 957 * @return A decoded representation of the server schema, or {@code null} if 958 * it is not available for some reason (e.g., the client does not 959 * have permission to read the server schema). 960 * 961 * @throws LDAPException If a problem occurs while obtaining the server 962 * schema. 963 */ 964 public static Schema getSchema(final LDAPConnection connection, 965 final String entryDN) 966 throws LDAPException 967 { 968 return getSchema(connection, entryDN, false); 969 } 970 971 972 973 /** 974 * Retrieves the directory server schema that governs the specified entry. 975 * In some servers, different portions of the DIT may be served by different 976 * schemas, and in such cases it will be necessary to provide the DN of the 977 * target entry in order to ensure that the appropriate schema which governs 978 * that entry is returned. For servers that support only a single schema, 979 * any entry DN (including that of the root DSE) should be sufficient. This 980 * method may optionally throw an exception if the retrieved schema contains 981 * one or more unparsable schema elements. 982 * 983 * @param connection The connection to use in order to 984 * retrieve the server schema. It must not 985 * be {@code null}. 986 * @param entryDN The DN of the entry for which to retrieve 987 * the governing schema. It may be 988 * {@code null} or an empty string in order 989 * to retrieve the schema that governs the 990 * server's root DSE. 991 * @param throwOnUnparsableElement Indicates whether to throw an exception 992 * if the schema entry that is retrieved has 993 * one or more unparsable schema elements. 994 * 995 * @return A decoded representation of the server schema, or {@code null} if 996 * it is not available for some reason (e.g., the client does not 997 * have permission to read the server schema). 998 * 999 * @throws LDAPException If a problem occurs while obtaining the server 1000 * schema, or if the schema contains one or more 1001 * unparsable elements and 1002 * {@code throwOnUnparsableElement} is {@code true}. 1003 */ 1004 public static Schema getSchema(final LDAPConnection connection, 1005 final String entryDN, 1006 final boolean throwOnUnparsableElement) 1007 throws LDAPException 1008 { 1009 Validator.ensureNotNull(connection); 1010 1011 final String subschemaSubentryDN; 1012 if (entryDN == null) 1013 { 1014 subschemaSubentryDN = getSubschemaSubentryDN(connection, ""); 1015 } 1016 else 1017 { 1018 subschemaSubentryDN = getSubschemaSubentryDN(connection, entryDN); 1019 } 1020 1021 if (subschemaSubentryDN == null) 1022 { 1023 return null; 1024 } 1025 1026 final Entry schemaEntry = connection.searchForEntry(subschemaSubentryDN, 1027 SearchScope.BASE, 1028 Filter.createEqualityFilter("objectClass", "subschema"), 1029 SCHEMA_REQUEST_ATTRS); 1030 if (schemaEntry == null) 1031 { 1032 return null; 1033 } 1034 1035 if (throwOnUnparsableElement) 1036 { 1037 return parseSchemaEntry(schemaEntry); 1038 } 1039 else 1040 { 1041 return new Schema(schemaEntry); 1042 } 1043 } 1044 1045 1046 1047 /** 1048 * Reads schema information from one or more files containing the schema 1049 * represented in LDIF form, with the definitions represented in the form 1050 * described in section 4.1 of RFC 4512. Each file should contain a single 1051 * entry. Any unparsable schema elements will be silently ignored. 1052 * 1053 * @param schemaFiles The paths to the LDIF files containing the schema 1054 * information to be read. At least one file must be 1055 * specified. If multiple files are specified, then they 1056 * will be processed in the order in which they have been 1057 * listed. 1058 * 1059 * @return The schema read from the specified schema files, or {@code null} 1060 * if none of the files contains any LDIF data to be read. 1061 * 1062 * @throws IOException If a problem occurs while attempting to read from 1063 * any of the specified files. 1064 * 1065 * @throws LDIFException If a problem occurs while attempting to parse the 1066 * contents of any of the schema files. 1067 */ 1068 public static Schema getSchema(final String... schemaFiles) 1069 throws IOException, LDIFException 1070 { 1071 Validator.ensureNotNull(schemaFiles); 1072 Validator.ensureFalse(schemaFiles.length == 0); 1073 1074 final ArrayList<File> files = new ArrayList<>(schemaFiles.length); 1075 for (final String s : schemaFiles) 1076 { 1077 files.add(new File(s)); 1078 } 1079 1080 return getSchema(files); 1081 } 1082 1083 1084 1085 /** 1086 * Reads schema information from one or more files containing the schema 1087 * represented in LDIF form, with the definitions represented in the form 1088 * described in section 4.1 of RFC 4512. Each file should contain a single 1089 * entry. Any unparsable schema elements will be silently ignored. 1090 * 1091 * @param schemaFiles The paths to the LDIF files containing the schema 1092 * information to be read. At least one file must be 1093 * specified. If multiple files are specified, then they 1094 * will be processed in the order in which they have been 1095 * listed. 1096 * 1097 * @return The schema read from the specified schema files, or {@code null} 1098 * if none of the files contains any LDIF data to be read. 1099 * 1100 * @throws IOException If a problem occurs while attempting to read from 1101 * any of the specified files. 1102 * 1103 * @throws LDIFException If a problem occurs while attempting to parse the 1104 * contents of any of the schema files. 1105 */ 1106 public static Schema getSchema(final File... schemaFiles) 1107 throws IOException, LDIFException 1108 { 1109 Validator.ensureNotNull(schemaFiles); 1110 Validator.ensureFalse(schemaFiles.length == 0); 1111 1112 return getSchema(Arrays.asList(schemaFiles)); 1113 } 1114 1115 1116 1117 /** 1118 * Reads schema information from one or more files containing the schema 1119 * represented in LDIF form, with the definitions represented in the form 1120 * described in section 4.1 of RFC 4512. Each file should contain a single 1121 * entry. Any unparsable schema elements will be silently ignored. 1122 * 1123 * @param schemaFiles The paths to the LDIF files containing the schema 1124 * information to be read. At least one file must be 1125 * specified. If multiple files are specified, then they 1126 * will be processed in the order in which they have been 1127 * listed. 1128 * 1129 * @return The schema read from the specified schema files, or {@code null} 1130 * if none of the files contains any LDIF data to be read. 1131 * 1132 * @throws IOException If a problem occurs while attempting to read from 1133 * any of the specified files. 1134 * 1135 * @throws LDIFException If a problem occurs while attempting to parse the 1136 * contents of any of the schema files. 1137 */ 1138 public static Schema getSchema(final List<File> schemaFiles) 1139 throws IOException, LDIFException 1140 { 1141 return getSchema(schemaFiles, false); 1142 } 1143 1144 1145 1146 /** 1147 * Reads schema information from one or more files containing the schema 1148 * represented in LDIF form, with the definitions represented in the form 1149 * described in section 4.1 of RFC 4512. Each file should contain a single 1150 * entry. 1151 * 1152 * @param schemaFiles The paths to the LDIF files containing 1153 * the schema information to be read. At 1154 * least one file must be specified. If 1155 * multiple files are specified, then they 1156 * will be processed in the order in which 1157 * they have been listed. 1158 * @param throwOnUnparsableElement Indicates whether to throw an exception 1159 * if the schema entry that is retrieved has 1160 * one or more unparsable schema elements. 1161 * 1162 * @return The schema read from the specified schema files, or {@code null} 1163 * if none of the files contains any LDIF data to be read. 1164 * 1165 * @throws IOException If a problem occurs while attempting to read from 1166 * any of the specified files. 1167 * 1168 * @throws LDIFException If a problem occurs while attempting to parse the 1169 * contents of any of the schema files. If 1170 * {@code throwOnUnparsableElement} is {@code true}, 1171 * then this may also be thrown if any of the schema 1172 * files contains any unparsable schema elements. 1173 */ 1174 public static Schema getSchema(final List<File> schemaFiles, 1175 final boolean throwOnUnparsableElement) 1176 throws IOException, LDIFException 1177 { 1178 Validator.ensureNotNull(schemaFiles); 1179 Validator.ensureFalse(schemaFiles.isEmpty()); 1180 1181 Entry schemaEntry = null; 1182 for (final File f : schemaFiles) 1183 { 1184 final LDIFReader ldifReader = new LDIFReader(f); 1185 1186 try 1187 { 1188 final Entry e = ldifReader.readEntry(); 1189 if (e == null) 1190 { 1191 continue; 1192 } 1193 1194 e.addAttribute("objectClass", "top", "ldapSubentry", "subschema"); 1195 1196 if (schemaEntry == null) 1197 { 1198 schemaEntry = e; 1199 } 1200 else 1201 { 1202 for (final Attribute a : e.getAttributes()) 1203 { 1204 schemaEntry.addAttribute(a); 1205 } 1206 } 1207 } 1208 finally 1209 { 1210 ldifReader.close(); 1211 } 1212 } 1213 1214 if (schemaEntry == null) 1215 { 1216 return null; 1217 } 1218 1219 if (throwOnUnparsableElement) 1220 { 1221 try 1222 { 1223 return parseSchemaEntry(schemaEntry); 1224 } 1225 catch (final LDAPException e) 1226 { 1227 Debug.debugException(e); 1228 throw new LDIFException(e.getMessage(), 0, false, e); 1229 } 1230 } 1231 else 1232 { 1233 return new Schema(schemaEntry); 1234 } 1235 } 1236 1237 1238 1239 /** 1240 * Reads schema information from the provided input stream. The information 1241 * should be in LDIF form, with the definitions represented in the form 1242 * described in section 4.1 of RFC 4512. Only a single entry will be read 1243 * from the input stream, and it will be closed at the end of this method. 1244 * 1245 * @param inputStream The input stream from which the schema entry will be 1246 * read. It must not be {@code null}, and it will be 1247 * closed when this method returns. 1248 * 1249 * @return The schema read from the provided input stream, or {@code null} if 1250 * the end of the input stream is reached without reading any data. 1251 * 1252 * @throws IOException If a problem is encountered while attempting to read 1253 * from the provided input stream. 1254 * 1255 * @throws LDIFException If a problem occurs while attempting to parse the 1256 * data read as LDIF. 1257 */ 1258 public static Schema getSchema(final InputStream inputStream) 1259 throws IOException, LDIFException 1260 { 1261 Validator.ensureNotNull(inputStream); 1262 1263 final LDIFReader ldifReader = new LDIFReader(inputStream); 1264 1265 try 1266 { 1267 final Entry e = ldifReader.readEntry(); 1268 if (e == null) 1269 { 1270 return null; 1271 } 1272 else 1273 { 1274 return new Schema(e); 1275 } 1276 } 1277 finally 1278 { 1279 ldifReader.close(); 1280 } 1281 } 1282 1283 1284 1285 /** 1286 * Retrieves a schema object that contains definitions for a number of 1287 * standard attribute types and object classes from LDAP-related RFCs and 1288 * Internet Drafts. 1289 * 1290 * @return A schema object that contains definitions for a number of standard 1291 * attribute types and object classes from LDAP-related RFCs and 1292 * Internet Drafts. 1293 * 1294 * @throws LDAPException If a problem occurs while attempting to obtain or 1295 * parse the default standard schema definitions. 1296 */ 1297 public static Schema getDefaultStandardSchema() 1298 throws LDAPException 1299 { 1300 final Schema s = DEFAULT_STANDARD_SCHEMA.get(); 1301 if (s != null) 1302 { 1303 return s; 1304 } 1305 1306 synchronized (DEFAULT_STANDARD_SCHEMA) 1307 { 1308 try 1309 { 1310 final ClassLoader classLoader = Schema.class.getClassLoader(); 1311 final InputStream inputStream = 1312 classLoader.getResourceAsStream(DEFAULT_SCHEMA_RESOURCE_PATH); 1313 final LDIFReader ldifReader = new LDIFReader(inputStream); 1314 final Entry schemaEntry = ldifReader.readEntry(); 1315 ldifReader.close(); 1316 1317 final Schema schema = new Schema(schemaEntry); 1318 DEFAULT_STANDARD_SCHEMA.set(schema); 1319 return schema; 1320 } 1321 catch (final Exception e) 1322 { 1323 Debug.debugException(e); 1324 throw new LDAPException(ResultCode.LOCAL_ERROR, 1325 ERR_SCHEMA_CANNOT_LOAD_DEFAULT_DEFINITIONS.get( 1326 StaticUtils.getExceptionMessage(e)), 1327 e); 1328 } 1329 } 1330 } 1331 1332 1333 1334 /** 1335 * Retrieves a schema containing all of the elements of each of the provided 1336 * schemas. 1337 * 1338 * @param schemas The schemas to be merged. It must not be {@code null} or 1339 * empty. 1340 * 1341 * @return A merged representation of the provided schemas. 1342 */ 1343 public static Schema mergeSchemas(final Schema... schemas) 1344 { 1345 if ((schemas == null) || (schemas.length == 0)) 1346 { 1347 return null; 1348 } 1349 else if (schemas.length == 1) 1350 { 1351 return schemas[0]; 1352 } 1353 1354 final LinkedHashMap<String,String> asMap = new LinkedHashMap<>(100); 1355 final LinkedHashMap<String,String> atMap = new LinkedHashMap<>(100); 1356 final LinkedHashMap<String,String> dcrMap = new LinkedHashMap<>(10); 1357 final LinkedHashMap<Integer,String> dsrMap = new LinkedHashMap<>(10); 1358 final LinkedHashMap<String,String> mrMap = new LinkedHashMap<>(100); 1359 final LinkedHashMap<String,String> mruMap = new LinkedHashMap<>(10); 1360 final LinkedHashMap<String,String> nfMap = new LinkedHashMap<>(10); 1361 final LinkedHashMap<String,String> ocMap = new LinkedHashMap<>(100); 1362 1363 for (final Schema s : schemas) 1364 { 1365 for (final AttributeSyntaxDefinition as : s.asSet) 1366 { 1367 asMap.put(StaticUtils.toLowerCase(as.getOID()), as.toString()); 1368 } 1369 1370 for (final AttributeTypeDefinition at : s.atSet) 1371 { 1372 atMap.put(StaticUtils.toLowerCase(at.getOID()), at.toString()); 1373 } 1374 1375 for (final DITContentRuleDefinition dcr : s.dcrSet) 1376 { 1377 dcrMap.put(StaticUtils.toLowerCase(dcr.getOID()), dcr.toString()); 1378 } 1379 1380 for (final DITStructureRuleDefinition dsr : s.dsrSet) 1381 { 1382 dsrMap.put(dsr.getRuleID(), dsr.toString()); 1383 } 1384 1385 for (final MatchingRuleDefinition mr : s.mrSet) 1386 { 1387 mrMap.put(StaticUtils.toLowerCase(mr.getOID()), mr.toString()); 1388 } 1389 1390 for (final MatchingRuleUseDefinition mru : s.mruSet) 1391 { 1392 mruMap.put(StaticUtils.toLowerCase(mru.getOID()), mru.toString()); 1393 } 1394 1395 for (final NameFormDefinition nf : s.nfSet) 1396 { 1397 nfMap.put(StaticUtils.toLowerCase(nf.getOID()), nf.toString()); 1398 } 1399 1400 for (final ObjectClassDefinition oc : s.ocSet) 1401 { 1402 ocMap.put(StaticUtils.toLowerCase(oc.getOID()), oc.toString()); 1403 } 1404 } 1405 1406 final Entry e = new Entry(schemas[0].getSchemaEntry().getDN()); 1407 1408 final Attribute ocAttr = 1409 schemas[0].getSchemaEntry().getObjectClassAttribute(); 1410 if (ocAttr == null) 1411 { 1412 e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema"); 1413 } 1414 else 1415 { 1416 e.addAttribute(ocAttr); 1417 } 1418 1419 if (! asMap.isEmpty()) 1420 { 1421 final String[] values = new String[asMap.size()]; 1422 e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values)); 1423 } 1424 1425 if (! mrMap.isEmpty()) 1426 { 1427 final String[] values = new String[mrMap.size()]; 1428 e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values)); 1429 } 1430 1431 if (! atMap.isEmpty()) 1432 { 1433 final String[] values = new String[atMap.size()]; 1434 e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values)); 1435 } 1436 1437 if (! ocMap.isEmpty()) 1438 { 1439 final String[] values = new String[ocMap.size()]; 1440 e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values)); 1441 } 1442 1443 if (! dcrMap.isEmpty()) 1444 { 1445 final String[] values = new String[dcrMap.size()]; 1446 e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values)); 1447 } 1448 1449 if (! dsrMap.isEmpty()) 1450 { 1451 final String[] values = new String[dsrMap.size()]; 1452 e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values)); 1453 } 1454 1455 if (! nfMap.isEmpty()) 1456 { 1457 final String[] values = new String[nfMap.size()]; 1458 e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values)); 1459 } 1460 1461 if (! mruMap.isEmpty()) 1462 { 1463 final String[] values = new String[mruMap.size()]; 1464 e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values)); 1465 } 1466 1467 return new Schema(e); 1468 } 1469 1470 1471 1472 /** 1473 * Retrieves the entry used to create this schema object. 1474 * 1475 * @return The entry used to create this schema object. 1476 */ 1477 public ReadOnlyEntry getSchemaEntry() 1478 { 1479 return schemaEntry; 1480 } 1481 1482 1483 1484 /** 1485 * Retrieves the value of the subschemaSubentry attribute from the specified 1486 * entry using the provided connection. 1487 * 1488 * @param connection The connection to use in order to perform the search. 1489 * It must not be {@code null}. 1490 * @param entryDN The DN of the entry from which to retrieve the 1491 * subschemaSubentry attribute. It may be {@code null} or 1492 * an empty string in order to retrieve the value from the 1493 * server's root DSE. 1494 * 1495 * @return The value of the subschemaSubentry attribute from the specified 1496 * entry, or {@code null} if it is not available for some reason 1497 * (e.g., the client does not have permission to read the target 1498 * entry or the subschemaSubentry attribute). 1499 * 1500 * @throws LDAPException If a problem occurs while attempting to retrieve 1501 * the specified entry. 1502 */ 1503 public static String getSubschemaSubentryDN(final LDAPConnection connection, 1504 final String entryDN) 1505 throws LDAPException 1506 { 1507 Validator.ensureNotNull(connection); 1508 1509 final Entry e; 1510 if (entryDN == null) 1511 { 1512 e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS); 1513 } 1514 else 1515 { 1516 e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS); 1517 } 1518 1519 if (e == null) 1520 { 1521 return null; 1522 } 1523 1524 return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY); 1525 } 1526 1527 1528 1529 /** 1530 * Retrieves the set of attribute syntax definitions contained in the server 1531 * schema. 1532 * 1533 * @return The set of attribute syntax definitions contained in the server 1534 * schema. 1535 */ 1536 public Set<AttributeSyntaxDefinition> getAttributeSyntaxes() 1537 { 1538 return asSet; 1539 } 1540 1541 1542 1543 /** 1544 * Retrieves the attribute syntax with the specified OID from the server 1545 * schema. 1546 * 1547 * @param oid The OID of the attribute syntax to retrieve. It must not be 1548 * {@code null}. It may optionally include a minimum upper bound 1549 * (as may appear when the syntax OID is included in an attribute 1550 * type definition), but if it does then that portion will be 1551 * ignored when retrieving the attribute syntax. 1552 * 1553 * @return The requested attribute syntax, or {@code null} if there is no 1554 * such syntax defined in the server schema. 1555 */ 1556 public AttributeSyntaxDefinition getAttributeSyntax(final String oid) 1557 { 1558 Validator.ensureNotNull(oid); 1559 1560 final String lowerOID = StaticUtils.toLowerCase(oid); 1561 final int curlyPos = lowerOID.indexOf('{'); 1562 1563 if (curlyPos > 0) 1564 { 1565 return asMap.get(lowerOID.substring(0, curlyPos)); 1566 } 1567 else 1568 { 1569 return asMap.get(lowerOID); 1570 } 1571 } 1572 1573 1574 1575 /** 1576 * Retrieves the set of attribute type definitions contained in the server 1577 * schema. 1578 * 1579 * @return The set of attribute type definitions contained in the server 1580 * schema. 1581 */ 1582 public Set<AttributeTypeDefinition> getAttributeTypes() 1583 { 1584 return atSet; 1585 } 1586 1587 1588 1589 /** 1590 * Retrieves the set of operational attribute type definitions (i.e., those 1591 * definitions with a usage of directoryOperation, distributedOperation, or 1592 * dSAOperation) contained in the server schema. 1593 * 1594 * @return The set of operational attribute type definitions contained in the 1595 * server schema. 1596 */ 1597 public Set<AttributeTypeDefinition> getOperationalAttributeTypes() 1598 { 1599 return operationalATSet; 1600 } 1601 1602 1603 1604 /** 1605 * Retrieves the set of user attribute type definitions (i.e., those 1606 * definitions with a usage of userApplications) contained in the server 1607 * schema. 1608 * 1609 * @return The set of user attribute type definitions contained in the server 1610 * schema. 1611 */ 1612 public Set<AttributeTypeDefinition> getUserAttributeTypes() 1613 { 1614 return userATSet; 1615 } 1616 1617 1618 1619 /** 1620 * Retrieves the attribute type with the specified name or OID from the server 1621 * schema. 1622 * 1623 * @param name The name or OID of the attribute type to retrieve. It must 1624 * not be {@code null}. 1625 * 1626 * @return The requested attribute type, or {@code null} if there is no 1627 * such attribute type defined in the server schema. 1628 */ 1629 public AttributeTypeDefinition getAttributeType(final String name) 1630 { 1631 Validator.ensureNotNull(name); 1632 1633 return atMap.get(StaticUtils.toLowerCase(name)); 1634 } 1635 1636 1637 1638 /** 1639 * Retrieves a list of all subordinate attribute type definitions for the 1640 * provided attribute type definition. 1641 * 1642 * @param d The attribute type definition for which to retrieve all 1643 * subordinate attribute types. It must not be {@code null}. 1644 * 1645 * @return A list of all subordinate attribute type definitions for the 1646 * provided attribute type definition, or an empty list if it does 1647 * not have any subordinate types or the provided attribute type is 1648 * not defined in the schema. 1649 */ 1650 public List<AttributeTypeDefinition> getSubordinateAttributeTypes( 1651 final AttributeTypeDefinition d) 1652 { 1653 Validator.ensureNotNull(d); 1654 1655 final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d); 1656 if (l == null) 1657 { 1658 return Collections.emptyList(); 1659 } 1660 else 1661 { 1662 return Collections.unmodifiableList(l); 1663 } 1664 } 1665 1666 1667 1668 /** 1669 * Retrieves the set of DIT content rule definitions contained in the server 1670 * schema. 1671 * 1672 * @return The set of DIT content rule definitions contained in the server 1673 * schema. 1674 */ 1675 public Set<DITContentRuleDefinition> getDITContentRules() 1676 { 1677 return dcrSet; 1678 } 1679 1680 1681 1682 /** 1683 * Retrieves the DIT content rule with the specified name or OID from the 1684 * server schema. 1685 * 1686 * @param name The name or OID of the DIT content rule to retrieve. It must 1687 * not be {@code null}. 1688 * 1689 * @return The requested DIT content rule, or {@code null} if there is no 1690 * such rule defined in the server schema. 1691 */ 1692 public DITContentRuleDefinition getDITContentRule(final String name) 1693 { 1694 Validator.ensureNotNull(name); 1695 1696 return dcrMap.get(StaticUtils.toLowerCase(name)); 1697 } 1698 1699 1700 1701 /** 1702 * Retrieves the set of DIT structure rule definitions contained in the server 1703 * schema. 1704 * 1705 * @return The set of DIT structure rule definitions contained in the server 1706 * schema. 1707 */ 1708 public Set<DITStructureRuleDefinition> getDITStructureRules() 1709 { 1710 return dsrSet; 1711 } 1712 1713 1714 1715 /** 1716 * Retrieves the DIT content rule with the specified rule ID from the server 1717 * schema. 1718 * 1719 * @param ruleID The rule ID for the DIT structure rule to retrieve. 1720 * 1721 * @return The requested DIT structure rule, or {@code null} if there is no 1722 * such rule defined in the server schema. 1723 */ 1724 public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID) 1725 { 1726 return dsrMapByID.get(ruleID); 1727 } 1728 1729 1730 1731 /** 1732 * Retrieves the DIT content rule with the specified name from the server 1733 * schema. 1734 * 1735 * @param ruleName The name of the DIT structure rule to retrieve. It must 1736 * not be {@code null}. 1737 * 1738 * @return The requested DIT structure rule, or {@code null} if there is no 1739 * such rule defined in the server schema. 1740 */ 1741 public DITStructureRuleDefinition getDITStructureRuleByName( 1742 final String ruleName) 1743 { 1744 Validator.ensureNotNull(ruleName); 1745 1746 return dsrMapByName.get(StaticUtils.toLowerCase(ruleName)); 1747 } 1748 1749 1750 1751 /** 1752 * Retrieves the DIT content rule associated with the specified name form from 1753 * the server schema. 1754 * 1755 * @param nameForm The name or OID of the name form for which to retrieve 1756 * the associated DIT structure rule. 1757 * 1758 * @return The requested DIT structure rule, or {@code null} if there is no 1759 * such rule defined in the server schema. 1760 */ 1761 public DITStructureRuleDefinition getDITStructureRuleByNameForm( 1762 final String nameForm) 1763 { 1764 Validator.ensureNotNull(nameForm); 1765 1766 return dsrMapByNameForm.get(StaticUtils.toLowerCase(nameForm)); 1767 } 1768 1769 1770 1771 /** 1772 * Retrieves the set of matching rule definitions contained in the server 1773 * schema. 1774 * 1775 * @return The set of matching rule definitions contained in the server 1776 * schema. 1777 */ 1778 public Set<MatchingRuleDefinition> getMatchingRules() 1779 { 1780 return mrSet; 1781 } 1782 1783 1784 1785 /** 1786 * Retrieves the matching rule with the specified name or OID from the server 1787 * schema. 1788 * 1789 * @param name The name or OID of the matching rule to retrieve. It must 1790 * not be {@code null}. 1791 * 1792 * @return The requested matching rule, or {@code null} if there is no 1793 * such rule defined in the server schema. 1794 */ 1795 public MatchingRuleDefinition getMatchingRule(final String name) 1796 { 1797 Validator.ensureNotNull(name); 1798 1799 return mrMap.get(StaticUtils.toLowerCase(name)); 1800 } 1801 1802 1803 1804 /** 1805 * Retrieves the set of matching rule use definitions contained in the server 1806 * schema. 1807 * 1808 * @return The set of matching rule use definitions contained in the server 1809 * schema. 1810 */ 1811 public Set<MatchingRuleUseDefinition> getMatchingRuleUses() 1812 { 1813 return mruSet; 1814 } 1815 1816 1817 1818 /** 1819 * Retrieves the matching rule use with the specified name or OID from the 1820 * server schema. 1821 * 1822 * @param name The name or OID of the matching rule use to retrieve. It 1823 * must not be {@code null}. 1824 * 1825 * @return The requested matching rule, or {@code null} if there is no 1826 * such matching rule use defined in the server schema. 1827 */ 1828 public MatchingRuleUseDefinition getMatchingRuleUse(final String name) 1829 { 1830 Validator.ensureNotNull(name); 1831 1832 return mruMap.get(StaticUtils.toLowerCase(name)); 1833 } 1834 1835 1836 1837 /** 1838 * Retrieves the set of name form definitions contained in the server schema. 1839 * 1840 * @return The set of name form definitions contained in the server schema. 1841 */ 1842 public Set<NameFormDefinition> getNameForms() 1843 { 1844 return nfSet; 1845 } 1846 1847 1848 1849 /** 1850 * Retrieves the name form with the specified name or OID from the server 1851 * schema. 1852 * 1853 * @param name The name or OID of the name form to retrieve. It must not be 1854 * {@code null}. 1855 * 1856 * @return The requested name form, or {@code null} if there is no 1857 * such rule defined in the server schema. 1858 */ 1859 public NameFormDefinition getNameFormByName(final String name) 1860 { 1861 Validator.ensureNotNull(name); 1862 1863 return nfMapByName.get(StaticUtils.toLowerCase(name)); 1864 } 1865 1866 1867 1868 /** 1869 * Retrieves the name form associated with the specified structural object 1870 * class from the server schema. 1871 * 1872 * @param objectClass The name or OID of the structural object class for 1873 * which to retrieve the associated name form. It must 1874 * not be {@code null}. 1875 * 1876 * @return The requested name form, or {@code null} if there is no 1877 * such rule defined in the server schema. 1878 */ 1879 public NameFormDefinition getNameFormByObjectClass(final String objectClass) 1880 { 1881 Validator.ensureNotNull(objectClass); 1882 1883 return nfMapByOC.get(StaticUtils.toLowerCase(objectClass)); 1884 } 1885 1886 1887 1888 /** 1889 * Retrieves the set of object class definitions contained in the server 1890 * schema. 1891 * 1892 * @return The set of object class definitions contained in the server 1893 * schema. 1894 */ 1895 public Set<ObjectClassDefinition> getObjectClasses() 1896 { 1897 return ocSet; 1898 } 1899 1900 1901 1902 /** 1903 * Retrieves the set of abstract object class definitions contained in the 1904 * server schema. 1905 * 1906 * @return The set of abstract object class definitions contained in the 1907 * server schema. 1908 */ 1909 public Set<ObjectClassDefinition> getAbstractObjectClasses() 1910 { 1911 return abstractOCSet; 1912 } 1913 1914 1915 1916 /** 1917 * Retrieves the set of auxiliary object class definitions contained in the 1918 * server schema. 1919 * 1920 * @return The set of auxiliary object class definitions contained in the 1921 * server schema. 1922 */ 1923 public Set<ObjectClassDefinition> getAuxiliaryObjectClasses() 1924 { 1925 return auxiliaryOCSet; 1926 } 1927 1928 1929 1930 /** 1931 * Retrieves the set of structural object class definitions contained in the 1932 * server schema. 1933 * 1934 * @return The set of structural object class definitions contained in the 1935 * server schema. 1936 */ 1937 public Set<ObjectClassDefinition> getStructuralObjectClasses() 1938 { 1939 return structuralOCSet; 1940 } 1941 1942 1943 1944 /** 1945 * Retrieves the object class with the specified name or OID from the server 1946 * schema. 1947 * 1948 * @param name The name or OID of the object class to retrieve. It must 1949 * not be {@code null}. 1950 * 1951 * @return The requested object class, or {@code null} if there is no such 1952 * class defined in the server schema. 1953 */ 1954 public ObjectClassDefinition getObjectClass(final String name) 1955 { 1956 Validator.ensureNotNull(name); 1957 1958 return ocMap.get(StaticUtils.toLowerCase(name)); 1959 } 1960 1961 1962 1963 /** 1964 * Retrieves a hash code for this schema object. 1965 * 1966 * @return A hash code for this schema object. 1967 */ 1968 @Override() 1969 public int hashCode() 1970 { 1971 int hc; 1972 try 1973 { 1974 hc = schemaEntry.getParsedDN().hashCode(); 1975 } 1976 catch (final Exception e) 1977 { 1978 Debug.debugException(e); 1979 hc = StaticUtils.toLowerCase(schemaEntry.getDN()).hashCode(); 1980 } 1981 1982 Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX); 1983 if (a != null) 1984 { 1985 hc += a.hashCode(); 1986 } 1987 1988 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE); 1989 if (a != null) 1990 { 1991 hc += a.hashCode(); 1992 } 1993 1994 a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE); 1995 if (a != null) 1996 { 1997 hc += a.hashCode(); 1998 } 1999 2000 a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS); 2001 if (a != null) 2002 { 2003 hc += a.hashCode(); 2004 } 2005 2006 a = schemaEntry.getAttribute(ATTR_NAME_FORM); 2007 if (a != null) 2008 { 2009 hc += a.hashCode(); 2010 } 2011 2012 a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE); 2013 if (a != null) 2014 { 2015 hc += a.hashCode(); 2016 } 2017 2018 a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE); 2019 if (a != null) 2020 { 2021 hc += a.hashCode(); 2022 } 2023 2024 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE); 2025 if (a != null) 2026 { 2027 hc += a.hashCode(); 2028 } 2029 2030 return hc; 2031 } 2032 2033 2034 2035 /** 2036 * Indicates whether the provided object is equal to this schema object. 2037 * 2038 * @param o The object for which to make the determination. 2039 * 2040 * @return {@code true} if the provided object is equal to this schema 2041 * object, or {@code false} if not. 2042 */ 2043 @Override() 2044 public boolean equals(final Object o) 2045 { 2046 if (o == null) 2047 { 2048 return false; 2049 } 2050 2051 if (o == this) 2052 { 2053 return true; 2054 } 2055 2056 if (! (o instanceof Schema)) 2057 { 2058 return false; 2059 } 2060 2061 final Schema s = (Schema) o; 2062 2063 try 2064 { 2065 if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN())) 2066 { 2067 return false; 2068 } 2069 } 2070 catch (final Exception e) 2071 { 2072 Debug.debugException(e); 2073 if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN())) 2074 { 2075 return false; 2076 } 2077 } 2078 2079 return (asSet.equals(s.asSet) && 2080 mrSet.equals(s.mrSet) && 2081 atSet.equals(s.atSet) && 2082 ocSet.equals(s.ocSet) && 2083 nfSet.equals(s.nfSet) && 2084 dcrSet.equals(s.dcrSet) && 2085 dsrSet.equals(s.dsrSet) && 2086 mruSet.equals(s.mruSet)); 2087 } 2088 2089 2090 2091 /** 2092 * Retrieves a string representation of the associated schema entry. 2093 * 2094 * @return A string representation of the associated schema entry. 2095 */ 2096 @Override() 2097 public String toString() 2098 { 2099 return schemaEntry.toString(); 2100 } 2101}