001/* 002 * Copyright 2007-2019 Ping Identity Corporation 003 * All Rights Reserved. 004 */ 005/* 006 * Copyright (C) 2008-2019 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<>(StaticUtils.computeMapCapacity(defs.length)); 367 final LinkedHashSet<AttributeSyntaxDefinition> s = 368 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(2*defs.length)); 407 final LinkedHashSet<AttributeTypeDefinition> s = 408 new LinkedHashSet<>(StaticUtils.computeMapCapacity(defs.length)); 409 final LinkedHashSet<AttributeTypeDefinition> sUser = 410 new LinkedHashSet<>(StaticUtils.computeMapCapacity(defs.length)); 411 final LinkedHashSet<AttributeTypeDefinition> sOperational = 412 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(defs.length)); 507 final LinkedHashMap<String,DITStructureRuleDefinition> mN = 508 new LinkedHashMap<>(StaticUtils.computeMapCapacity(defs.length)); 509 final LinkedHashMap<String,DITStructureRuleDefinition> mNF = 510 new LinkedHashMap<>(StaticUtils.computeMapCapacity(defs.length)); 511 final LinkedHashSet<DITStructureRuleDefinition> s = 512 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(2*defs.length)); 556 final LinkedHashSet<MatchingRuleDefinition> s = 557 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(2*defs.length)); 597 final LinkedHashSet<MatchingRuleUseDefinition> s = 598 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(2*defs.length)); 640 final LinkedHashMap<String,NameFormDefinition> mOC = 641 new LinkedHashMap<>(StaticUtils.computeMapCapacity(defs.length)); 642 final LinkedHashSet<NameFormDefinition> s = 643 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>(StaticUtils.computeMapCapacity(2*defs.length)); 688 final LinkedHashSet<ObjectClassDefinition> s = 689 new LinkedHashSet<>(StaticUtils.computeMapCapacity(defs.length)); 690 final LinkedHashSet<ObjectClassDefinition> sAbstract = 691 new LinkedHashSet<>(StaticUtils.computeMapCapacity(defs.length)); 692 final LinkedHashSet<ObjectClassDefinition> sAuxiliary = 693 new LinkedHashSet<>(StaticUtils.computeMapCapacity(defs.length)); 694 final LinkedHashSet<ObjectClassDefinition> sStructural = 695 new LinkedHashSet<>(StaticUtils.computeMapCapacity(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<>( 743 StaticUtils.computeMapCapacity(atSet.size())); 744 for (final AttributeTypeDefinition d : atSet) 745 { 746 AttributeTypeDefinition sup = d.getSuperiorType(this); 747 while (sup != null) 748 { 749 List<AttributeTypeDefinition> l = subAttrTypes.get(sup); 750 if (l == null) 751 { 752 l = new ArrayList<>(1); 753 subAttrTypes.put(sup, l); 754 } 755 l.add(d); 756 757 sup = sup.getSuperiorType(this); 758 } 759 } 760 subordinateAttributeTypes = Collections.unmodifiableMap(subAttrTypes); 761 } 762 763 764 765 /** 766 * Parses all schema elements contained in the provided entry. This method 767 * differs from the {@link #Schema(Entry)} constructor in that this method 768 * will throw an exception if it encounters any unparsable schema elements, 769 * while the constructor will silently ignore them. Alternately, the 770 * 'constructor that takes a bunch of maps can be used to 771 * 772 * @param schemaEntry The schema entry to parse. It must not be 773 * {@code null}. 774 * 775 * @return The schema entry that was parsed. 776 * 777 * @throws LDAPException If the provided entry contains any schema element 778 * definitions that cannot be parsed. 779 */ 780 public static Schema parseSchemaEntry(final Entry schemaEntry) 781 throws LDAPException 782 { 783 final Map<String,LDAPException> unparsableAttributeSyntaxes = 784 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 785 final Map<String,LDAPException> unparsableMatchingRules = 786 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 787 final Map<String,LDAPException> unparsableAttributeTypes = 788 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 789 final Map<String,LDAPException> unparsableObjectClasses = 790 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 791 final Map<String,LDAPException> unparsableDITContentRules = 792 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 793 final Map<String,LDAPException> unparsableDITStructureRules = 794 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 795 final Map<String,LDAPException> unparsableNameForms = 796 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 797 final Map<String,LDAPException> unparsableMatchingRuleUses = 798 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 799 800 final Schema schema = new Schema(schemaEntry, unparsableAttributeSyntaxes, 801 unparsableMatchingRules, unparsableAttributeTypes, 802 unparsableObjectClasses, unparsableDITContentRules, 803 unparsableDITStructureRules, unparsableNameForms, 804 unparsableMatchingRuleUses); 805 if (unparsableAttributeSyntaxes.isEmpty() && 806 unparsableMatchingRules.isEmpty() && 807 unparsableAttributeTypes.isEmpty() && 808 unparsableObjectClasses.isEmpty() && 809 unparsableDITContentRules.isEmpty() && 810 unparsableDITStructureRules.isEmpty() && 811 unparsableNameForms.isEmpty() && 812 unparsableMatchingRuleUses.isEmpty()) 813 { 814 return schema; 815 } 816 817 final StringBuilder messageBuffer = new StringBuilder(); 818 for (final Map.Entry<String,LDAPException> e : 819 unparsableAttributeSyntaxes.entrySet()) 820 { 821 appendErrorMessage(messageBuffer, 822 ERR_SCHEMA_UNPARSABLE_AS.get(ATTR_ATTRIBUTE_SYNTAX, e.getKey(), 823 StaticUtils.getExceptionMessage(e.getValue()))); 824 } 825 826 for (final Map.Entry<String,LDAPException> e : 827 unparsableMatchingRules.entrySet()) 828 { 829 appendErrorMessage(messageBuffer, 830 ERR_SCHEMA_UNPARSABLE_MR.get(ATTR_MATCHING_RULE, e.getKey(), 831 StaticUtils.getExceptionMessage(e.getValue()))); 832 } 833 834 for (final Map.Entry<String,LDAPException> e : 835 unparsableAttributeTypes.entrySet()) 836 { 837 appendErrorMessage(messageBuffer, 838 ERR_SCHEMA_UNPARSABLE_AT.get(ATTR_ATTRIBUTE_TYPE, e.getKey(), 839 StaticUtils.getExceptionMessage(e.getValue()))); 840 } 841 842 for (final Map.Entry<String,LDAPException> e : 843 unparsableObjectClasses.entrySet()) 844 { 845 appendErrorMessage(messageBuffer, 846 ERR_SCHEMA_UNPARSABLE_OC.get(ATTR_OBJECT_CLASS, e.getKey(), 847 StaticUtils.getExceptionMessage(e.getValue()))); 848 } 849 850 for (final Map.Entry<String,LDAPException> e : 851 unparsableDITContentRules.entrySet()) 852 { 853 appendErrorMessage(messageBuffer, 854 ERR_SCHEMA_UNPARSABLE_DCR.get(ATTR_DIT_CONTENT_RULE, e.getKey(), 855 StaticUtils.getExceptionMessage(e.getValue()))); 856 } 857 858 for (final Map.Entry<String,LDAPException> e : 859 unparsableDITStructureRules.entrySet()) 860 { 861 appendErrorMessage(messageBuffer, 862 ERR_SCHEMA_UNPARSABLE_DSR.get(ATTR_DIT_STRUCTURE_RULE, e.getKey(), 863 StaticUtils.getExceptionMessage(e.getValue()))); 864 } 865 866 for (final Map.Entry<String,LDAPException> e : 867 unparsableNameForms.entrySet()) 868 { 869 appendErrorMessage(messageBuffer, 870 ERR_SCHEMA_UNPARSABLE_NF.get(ATTR_NAME_FORM, e.getKey(), 871 StaticUtils.getExceptionMessage(e.getValue()))); 872 } 873 874 for (final Map.Entry<String,LDAPException> e : 875 unparsableMatchingRuleUses.entrySet()) 876 { 877 appendErrorMessage(messageBuffer, 878 ERR_SCHEMA_UNPARSABLE_MRU.get(ATTR_MATCHING_RULE_USE, e.getKey(), 879 StaticUtils.getExceptionMessage(e.getValue()))); 880 } 881 882 throw new LDAPException(ResultCode.INVALID_ATTRIBUTE_SYNTAX, 883 messageBuffer.toString()); 884 } 885 886 887 888 /** 889 * Appends the provided message to the given buffer, adding spaces and 890 * punctuation if necessary. 891 * 892 * @param buffer The buffer to which the message should be appended. 893 * @param message The message to append to the buffer. 894 */ 895 private static void appendErrorMessage(final StringBuilder buffer, 896 final String message) 897 { 898 final int length = buffer.length(); 899 if (length > 0) 900 { 901 if (buffer.charAt(length - 1) == '.') 902 { 903 buffer.append(" "); 904 } 905 else 906 { 907 buffer.append(". "); 908 } 909 } 910 911 buffer.append(message); 912 } 913 914 915 916 /** 917 * Retrieves the directory server schema over the provided connection. The 918 * root DSE will first be retrieved in order to get its subschemaSubentry DN, 919 * and then that entry will be retrieved from the server and its contents 920 * decoded as schema elements. This should be sufficient for directories that 921 * only provide a single schema, but for directories with multiple schemas it 922 * may be necessary to specify the DN of an entry for which to retrieve the 923 * subschema subentry. Any unparsable schema elements will be silently 924 * ignored. 925 * 926 * @param connection The connection to use in order to retrieve the server 927 * schema. It must not be {@code null}. 928 * 929 * @return A decoded representation of the server schema. 930 * 931 * @throws LDAPException If a problem occurs while obtaining the server 932 * schema. 933 */ 934 public static Schema getSchema(final LDAPConnection connection) 935 throws LDAPException 936 { 937 return getSchema(connection, ""); 938 } 939 940 941 942 /** 943 * Retrieves the directory server schema that governs the specified entry. 944 * In some servers, different portions of the DIT may be served by different 945 * schemas, and in such cases it will be necessary to provide the DN of the 946 * target entry in order to ensure that the appropriate schema which governs 947 * that entry is returned. For servers that support only a single schema, 948 * any entry DN (including that of the root DSE) should be sufficient. Any 949 * unparsable schema elements will be silently ignored. 950 * 951 * @param connection The connection to use in order to retrieve the server 952 * schema. It must not be {@code null}. 953 * @param entryDN The DN of the entry for which to retrieve the governing 954 * schema. It may be {@code null} or an empty string in 955 * order to retrieve the schema that governs the server's 956 * root DSE. 957 * 958 * @return A decoded representation of the server schema, or {@code null} if 959 * it is not available for some reason (e.g., the client does not 960 * have permission to read the server schema). 961 * 962 * @throws LDAPException If a problem occurs while obtaining the server 963 * schema. 964 */ 965 public static Schema getSchema(final LDAPConnection connection, 966 final String entryDN) 967 throws LDAPException 968 { 969 return getSchema(connection, entryDN, false); 970 } 971 972 973 974 /** 975 * Retrieves the directory server schema that governs the specified entry. 976 * In some servers, different portions of the DIT may be served by different 977 * schemas, and in such cases it will be necessary to provide the DN of the 978 * target entry in order to ensure that the appropriate schema which governs 979 * that entry is returned. For servers that support only a single schema, 980 * any entry DN (including that of the root DSE) should be sufficient. This 981 * method may optionally throw an exception if the retrieved schema contains 982 * one or more unparsable schema elements. 983 * 984 * @param connection The connection to use in order to 985 * retrieve the server schema. It must not 986 * be {@code null}. 987 * @param entryDN The DN of the entry for which to retrieve 988 * the governing schema. It may be 989 * {@code null} or an empty string in order 990 * to retrieve the schema that governs the 991 * server's root DSE. 992 * @param throwOnUnparsableElement Indicates whether to throw an exception 993 * if the schema entry that is retrieved has 994 * one or more unparsable schema elements. 995 * 996 * @return A decoded representation of the server schema, or {@code null} if 997 * it is not available for some reason (e.g., the client does not 998 * have permission to read the server schema). 999 * 1000 * @throws LDAPException If a problem occurs while obtaining the server 1001 * schema, or if the schema contains one or more 1002 * unparsable elements and 1003 * {@code throwOnUnparsableElement} is {@code true}. 1004 */ 1005 public static Schema getSchema(final LDAPConnection connection, 1006 final String entryDN, 1007 final boolean throwOnUnparsableElement) 1008 throws LDAPException 1009 { 1010 Validator.ensureNotNull(connection); 1011 1012 final String subschemaSubentryDN; 1013 if (entryDN == null) 1014 { 1015 subschemaSubentryDN = getSubschemaSubentryDN(connection, ""); 1016 } 1017 else 1018 { 1019 subschemaSubentryDN = getSubschemaSubentryDN(connection, entryDN); 1020 } 1021 1022 if (subschemaSubentryDN == null) 1023 { 1024 return null; 1025 } 1026 1027 final Entry schemaEntry = connection.searchForEntry(subschemaSubentryDN, 1028 SearchScope.BASE, 1029 Filter.createEqualityFilter("objectClass", "subschema"), 1030 SCHEMA_REQUEST_ATTRS); 1031 if (schemaEntry == null) 1032 { 1033 return null; 1034 } 1035 1036 if (throwOnUnparsableElement) 1037 { 1038 return parseSchemaEntry(schemaEntry); 1039 } 1040 else 1041 { 1042 return new Schema(schemaEntry); 1043 } 1044 } 1045 1046 1047 1048 /** 1049 * Reads schema information from one or more files containing the schema 1050 * represented in LDIF form, with the definitions represented in the form 1051 * described in section 4.1 of RFC 4512. Each file should contain a single 1052 * entry. Any unparsable schema elements will be silently ignored. 1053 * 1054 * @param schemaFiles The paths to the LDIF files containing the schema 1055 * information to be read. At least one file must be 1056 * specified. If multiple files are specified, then they 1057 * will be processed in the order in which they have been 1058 * listed. 1059 * 1060 * @return The schema read from the specified schema files, or {@code null} 1061 * if none of the files contains any LDIF data to be read. 1062 * 1063 * @throws IOException If a problem occurs while attempting to read from 1064 * any of the specified files. 1065 * 1066 * @throws LDIFException If a problem occurs while attempting to parse the 1067 * contents of any of the schema files. 1068 */ 1069 public static Schema getSchema(final String... schemaFiles) 1070 throws IOException, LDIFException 1071 { 1072 Validator.ensureNotNull(schemaFiles); 1073 Validator.ensureFalse(schemaFiles.length == 0); 1074 1075 final ArrayList<File> files = new ArrayList<>(schemaFiles.length); 1076 for (final String s : schemaFiles) 1077 { 1078 files.add(new File(s)); 1079 } 1080 1081 return getSchema(files); 1082 } 1083 1084 1085 1086 /** 1087 * Reads schema information from one or more files containing the schema 1088 * represented in LDIF form, with the definitions represented in the form 1089 * described in section 4.1 of RFC 4512. Each file should contain a single 1090 * entry. Any unparsable schema elements will be silently ignored. 1091 * 1092 * @param schemaFiles The paths to the LDIF files containing the schema 1093 * information to be read. At least one file must be 1094 * specified. If multiple files are specified, then they 1095 * will be processed in the order in which they have been 1096 * listed. 1097 * 1098 * @return The schema read from the specified schema files, or {@code null} 1099 * if none of the files contains any LDIF data to be read. 1100 * 1101 * @throws IOException If a problem occurs while attempting to read from 1102 * any of the specified files. 1103 * 1104 * @throws LDIFException If a problem occurs while attempting to parse the 1105 * contents of any of the schema files. 1106 */ 1107 public static Schema getSchema(final File... schemaFiles) 1108 throws IOException, LDIFException 1109 { 1110 Validator.ensureNotNull(schemaFiles); 1111 Validator.ensureFalse(schemaFiles.length == 0); 1112 1113 return getSchema(Arrays.asList(schemaFiles)); 1114 } 1115 1116 1117 1118 /** 1119 * Reads schema information from one or more files containing the schema 1120 * represented in LDIF form, with the definitions represented in the form 1121 * described in section 4.1 of RFC 4512. Each file should contain a single 1122 * entry. Any unparsable schema elements will be silently ignored. 1123 * 1124 * @param schemaFiles The paths to the LDIF files containing the schema 1125 * information to be read. At least one file must be 1126 * specified. If multiple files are specified, then they 1127 * will be processed in the order in which they have been 1128 * listed. 1129 * 1130 * @return The schema read from the specified schema files, or {@code null} 1131 * if none of the files contains any LDIF data to be read. 1132 * 1133 * @throws IOException If a problem occurs while attempting to read from 1134 * any of the specified files. 1135 * 1136 * @throws LDIFException If a problem occurs while attempting to parse the 1137 * contents of any of the schema files. 1138 */ 1139 public static Schema getSchema(final List<File> schemaFiles) 1140 throws IOException, LDIFException 1141 { 1142 return getSchema(schemaFiles, false); 1143 } 1144 1145 1146 1147 /** 1148 * Reads schema information from one or more files containing the schema 1149 * represented in LDIF form, with the definitions represented in the form 1150 * described in section 4.1 of RFC 4512. Each file should contain a single 1151 * entry. 1152 * 1153 * @param schemaFiles The paths to the LDIF files containing 1154 * the schema information to be read. At 1155 * least one file must be specified. If 1156 * multiple files are specified, then they 1157 * will be processed in the order in which 1158 * they have been listed. 1159 * @param throwOnUnparsableElement Indicates whether to throw an exception 1160 * if the schema entry that is retrieved has 1161 * one or more unparsable schema elements. 1162 * 1163 * @return The schema read from the specified schema files, or {@code null} 1164 * if none of the files contains any LDIF data to be read. 1165 * 1166 * @throws IOException If a problem occurs while attempting to read from 1167 * any of the specified files. 1168 * 1169 * @throws LDIFException If a problem occurs while attempting to parse the 1170 * contents of any of the schema files. If 1171 * {@code throwOnUnparsableElement} is {@code true}, 1172 * then this may also be thrown if any of the schema 1173 * files contains any unparsable schema elements. 1174 */ 1175 public static Schema getSchema(final List<File> schemaFiles, 1176 final boolean throwOnUnparsableElement) 1177 throws IOException, LDIFException 1178 { 1179 Validator.ensureNotNull(schemaFiles); 1180 Validator.ensureFalse(schemaFiles.isEmpty()); 1181 1182 Entry schemaEntry = null; 1183 for (final File f : schemaFiles) 1184 { 1185 final LDIFReader ldifReader = new LDIFReader(f); 1186 1187 try 1188 { 1189 final Entry e = ldifReader.readEntry(); 1190 if (e == null) 1191 { 1192 continue; 1193 } 1194 1195 e.addAttribute("objectClass", "top", "ldapSubentry", "subschema"); 1196 1197 if (schemaEntry == null) 1198 { 1199 schemaEntry = e; 1200 } 1201 else 1202 { 1203 for (final Attribute a : e.getAttributes()) 1204 { 1205 schemaEntry.addAttribute(a); 1206 } 1207 } 1208 } 1209 finally 1210 { 1211 ldifReader.close(); 1212 } 1213 } 1214 1215 if (schemaEntry == null) 1216 { 1217 return null; 1218 } 1219 1220 if (throwOnUnparsableElement) 1221 { 1222 try 1223 { 1224 return parseSchemaEntry(schemaEntry); 1225 } 1226 catch (final LDAPException e) 1227 { 1228 Debug.debugException(e); 1229 throw new LDIFException(e.getMessage(), 0, false, e); 1230 } 1231 } 1232 else 1233 { 1234 return new Schema(schemaEntry); 1235 } 1236 } 1237 1238 1239 1240 /** 1241 * Reads schema information from the provided input stream. The information 1242 * should be in LDIF form, with the definitions represented in the form 1243 * described in section 4.1 of RFC 4512. Only a single entry will be read 1244 * from the input stream, and it will be closed at the end of this method. 1245 * 1246 * @param inputStream The input stream from which the schema entry will be 1247 * read. It must not be {@code null}, and it will be 1248 * closed when this method returns. 1249 * 1250 * @return The schema read from the provided input stream, or {@code null} if 1251 * the end of the input stream is reached without reading any data. 1252 * 1253 * @throws IOException If a problem is encountered while attempting to read 1254 * from the provided input stream. 1255 * 1256 * @throws LDIFException If a problem occurs while attempting to parse the 1257 * data read as LDIF. 1258 */ 1259 public static Schema getSchema(final InputStream inputStream) 1260 throws IOException, LDIFException 1261 { 1262 Validator.ensureNotNull(inputStream); 1263 1264 final LDIFReader ldifReader = new LDIFReader(inputStream); 1265 1266 try 1267 { 1268 final Entry e = ldifReader.readEntry(); 1269 if (e == null) 1270 { 1271 return null; 1272 } 1273 else 1274 { 1275 return new Schema(e); 1276 } 1277 } 1278 finally 1279 { 1280 ldifReader.close(); 1281 } 1282 } 1283 1284 1285 1286 /** 1287 * Retrieves a schema object that contains definitions for a number of 1288 * standard attribute types and object classes from LDAP-related RFCs and 1289 * Internet Drafts. 1290 * 1291 * @return A schema object that contains definitions for a number of standard 1292 * attribute types and object classes from LDAP-related RFCs and 1293 * Internet Drafts. 1294 * 1295 * @throws LDAPException If a problem occurs while attempting to obtain or 1296 * parse the default standard schema definitions. 1297 */ 1298 public static Schema getDefaultStandardSchema() 1299 throws LDAPException 1300 { 1301 final Schema s = DEFAULT_STANDARD_SCHEMA.get(); 1302 if (s != null) 1303 { 1304 return s; 1305 } 1306 1307 synchronized (DEFAULT_STANDARD_SCHEMA) 1308 { 1309 try 1310 { 1311 final ClassLoader classLoader = Schema.class.getClassLoader(); 1312 final InputStream inputStream = 1313 classLoader.getResourceAsStream(DEFAULT_SCHEMA_RESOURCE_PATH); 1314 final LDIFReader ldifReader = new LDIFReader(inputStream); 1315 final Entry schemaEntry = ldifReader.readEntry(); 1316 ldifReader.close(); 1317 1318 final Schema schema = new Schema(schemaEntry); 1319 DEFAULT_STANDARD_SCHEMA.set(schema); 1320 return schema; 1321 } 1322 catch (final Exception e) 1323 { 1324 Debug.debugException(e); 1325 throw new LDAPException(ResultCode.LOCAL_ERROR, 1326 ERR_SCHEMA_CANNOT_LOAD_DEFAULT_DEFINITIONS.get( 1327 StaticUtils.getExceptionMessage(e)), 1328 e); 1329 } 1330 } 1331 } 1332 1333 1334 1335 /** 1336 * Retrieves a schema containing all of the elements of each of the provided 1337 * schemas. 1338 * 1339 * @param schemas The schemas to be merged. It must not be {@code null} or 1340 * empty. 1341 * 1342 * @return A merged representation of the provided schemas. 1343 */ 1344 public static Schema mergeSchemas(final Schema... schemas) 1345 { 1346 if ((schemas == null) || (schemas.length == 0)) 1347 { 1348 return null; 1349 } 1350 else if (schemas.length == 1) 1351 { 1352 return schemas[0]; 1353 } 1354 1355 final LinkedHashMap<String,String> asMap = 1356 new LinkedHashMap<>(StaticUtils.computeMapCapacity(100)); 1357 final LinkedHashMap<String,String> atMap = 1358 new LinkedHashMap<>(StaticUtils.computeMapCapacity(100)); 1359 final LinkedHashMap<String,String> dcrMap = 1360 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 1361 final LinkedHashMap<Integer,String> dsrMap = 1362 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 1363 final LinkedHashMap<String,String> mrMap = 1364 new LinkedHashMap<>(StaticUtils.computeMapCapacity(100)); 1365 final LinkedHashMap<String,String> mruMap = 1366 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 1367 final LinkedHashMap<String,String> nfMap = 1368 new LinkedHashMap<>(StaticUtils.computeMapCapacity(10)); 1369 final LinkedHashMap<String,String> ocMap = 1370 new LinkedHashMap<>(StaticUtils.computeMapCapacity(100)); 1371 1372 for (final Schema s : schemas) 1373 { 1374 for (final AttributeSyntaxDefinition as : s.asSet) 1375 { 1376 asMap.put(StaticUtils.toLowerCase(as.getOID()), as.toString()); 1377 } 1378 1379 for (final AttributeTypeDefinition at : s.atSet) 1380 { 1381 atMap.put(StaticUtils.toLowerCase(at.getOID()), at.toString()); 1382 } 1383 1384 for (final DITContentRuleDefinition dcr : s.dcrSet) 1385 { 1386 dcrMap.put(StaticUtils.toLowerCase(dcr.getOID()), dcr.toString()); 1387 } 1388 1389 for (final DITStructureRuleDefinition dsr : s.dsrSet) 1390 { 1391 dsrMap.put(dsr.getRuleID(), dsr.toString()); 1392 } 1393 1394 for (final MatchingRuleDefinition mr : s.mrSet) 1395 { 1396 mrMap.put(StaticUtils.toLowerCase(mr.getOID()), mr.toString()); 1397 } 1398 1399 for (final MatchingRuleUseDefinition mru : s.mruSet) 1400 { 1401 mruMap.put(StaticUtils.toLowerCase(mru.getOID()), mru.toString()); 1402 } 1403 1404 for (final NameFormDefinition nf : s.nfSet) 1405 { 1406 nfMap.put(StaticUtils.toLowerCase(nf.getOID()), nf.toString()); 1407 } 1408 1409 for (final ObjectClassDefinition oc : s.ocSet) 1410 { 1411 ocMap.put(StaticUtils.toLowerCase(oc.getOID()), oc.toString()); 1412 } 1413 } 1414 1415 final Entry e = new Entry(schemas[0].getSchemaEntry().getDN()); 1416 1417 final Attribute ocAttr = 1418 schemas[0].getSchemaEntry().getObjectClassAttribute(); 1419 if (ocAttr == null) 1420 { 1421 e.addAttribute("objectClass", "top", "ldapSubEntry", "subschema"); 1422 } 1423 else 1424 { 1425 e.addAttribute(ocAttr); 1426 } 1427 1428 if (! asMap.isEmpty()) 1429 { 1430 final String[] values = new String[asMap.size()]; 1431 e.addAttribute(ATTR_ATTRIBUTE_SYNTAX, asMap.values().toArray(values)); 1432 } 1433 1434 if (! mrMap.isEmpty()) 1435 { 1436 final String[] values = new String[mrMap.size()]; 1437 e.addAttribute(ATTR_MATCHING_RULE, mrMap.values().toArray(values)); 1438 } 1439 1440 if (! atMap.isEmpty()) 1441 { 1442 final String[] values = new String[atMap.size()]; 1443 e.addAttribute(ATTR_ATTRIBUTE_TYPE, atMap.values().toArray(values)); 1444 } 1445 1446 if (! ocMap.isEmpty()) 1447 { 1448 final String[] values = new String[ocMap.size()]; 1449 e.addAttribute(ATTR_OBJECT_CLASS, ocMap.values().toArray(values)); 1450 } 1451 1452 if (! dcrMap.isEmpty()) 1453 { 1454 final String[] values = new String[dcrMap.size()]; 1455 e.addAttribute(ATTR_DIT_CONTENT_RULE, dcrMap.values().toArray(values)); 1456 } 1457 1458 if (! dsrMap.isEmpty()) 1459 { 1460 final String[] values = new String[dsrMap.size()]; 1461 e.addAttribute(ATTR_DIT_STRUCTURE_RULE, dsrMap.values().toArray(values)); 1462 } 1463 1464 if (! nfMap.isEmpty()) 1465 { 1466 final String[] values = new String[nfMap.size()]; 1467 e.addAttribute(ATTR_NAME_FORM, nfMap.values().toArray(values)); 1468 } 1469 1470 if (! mruMap.isEmpty()) 1471 { 1472 final String[] values = new String[mruMap.size()]; 1473 e.addAttribute(ATTR_MATCHING_RULE_USE, mruMap.values().toArray(values)); 1474 } 1475 1476 return new Schema(e); 1477 } 1478 1479 1480 1481 /** 1482 * Retrieves the entry used to create this schema object. 1483 * 1484 * @return The entry used to create this schema object. 1485 */ 1486 public ReadOnlyEntry getSchemaEntry() 1487 { 1488 return schemaEntry; 1489 } 1490 1491 1492 1493 /** 1494 * Retrieves the value of the subschemaSubentry attribute from the specified 1495 * entry using the provided connection. 1496 * 1497 * @param connection The connection to use in order to perform the search. 1498 * It must not be {@code null}. 1499 * @param entryDN The DN of the entry from which to retrieve the 1500 * subschemaSubentry attribute. It may be {@code null} or 1501 * an empty string in order to retrieve the value from the 1502 * server's root DSE. 1503 * 1504 * @return The value of the subschemaSubentry attribute from the specified 1505 * entry, or {@code null} if it is not available for some reason 1506 * (e.g., the client does not have permission to read the target 1507 * entry or the subschemaSubentry attribute). 1508 * 1509 * @throws LDAPException If a problem occurs while attempting to retrieve 1510 * the specified entry. 1511 */ 1512 public static String getSubschemaSubentryDN(final LDAPConnection connection, 1513 final String entryDN) 1514 throws LDAPException 1515 { 1516 Validator.ensureNotNull(connection); 1517 1518 final Entry e; 1519 if (entryDN == null) 1520 { 1521 e = connection.getEntry("", SUBSCHEMA_SUBENTRY_REQUEST_ATTRS); 1522 } 1523 else 1524 { 1525 e = connection.getEntry(entryDN, SUBSCHEMA_SUBENTRY_REQUEST_ATTRS); 1526 } 1527 1528 if (e == null) 1529 { 1530 return null; 1531 } 1532 1533 return e.getAttributeValue(ATTR_SUBSCHEMA_SUBENTRY); 1534 } 1535 1536 1537 1538 /** 1539 * Retrieves the set of attribute syntax definitions contained in the server 1540 * schema. 1541 * 1542 * @return The set of attribute syntax definitions contained in the server 1543 * schema. 1544 */ 1545 public Set<AttributeSyntaxDefinition> getAttributeSyntaxes() 1546 { 1547 return asSet; 1548 } 1549 1550 1551 1552 /** 1553 * Retrieves the attribute syntax with the specified OID from the server 1554 * schema. 1555 * 1556 * @param oid The OID of the attribute syntax to retrieve. It must not be 1557 * {@code null}. It may optionally include a minimum upper bound 1558 * (as may appear when the syntax OID is included in an attribute 1559 * type definition), but if it does then that portion will be 1560 * ignored when retrieving the attribute syntax. 1561 * 1562 * @return The requested attribute syntax, or {@code null} if there is no 1563 * such syntax defined in the server schema. 1564 */ 1565 public AttributeSyntaxDefinition getAttributeSyntax(final String oid) 1566 { 1567 Validator.ensureNotNull(oid); 1568 1569 final String lowerOID = StaticUtils.toLowerCase(oid); 1570 final int curlyPos = lowerOID.indexOf('{'); 1571 1572 if (curlyPos > 0) 1573 { 1574 return asMap.get(lowerOID.substring(0, curlyPos)); 1575 } 1576 else 1577 { 1578 return asMap.get(lowerOID); 1579 } 1580 } 1581 1582 1583 1584 /** 1585 * Retrieves the set of attribute type definitions contained in the server 1586 * schema. 1587 * 1588 * @return The set of attribute type definitions contained in the server 1589 * schema. 1590 */ 1591 public Set<AttributeTypeDefinition> getAttributeTypes() 1592 { 1593 return atSet; 1594 } 1595 1596 1597 1598 /** 1599 * Retrieves the set of operational attribute type definitions (i.e., those 1600 * definitions with a usage of directoryOperation, distributedOperation, or 1601 * dSAOperation) contained in the server schema. 1602 * 1603 * @return The set of operational attribute type definitions contained in the 1604 * server schema. 1605 */ 1606 public Set<AttributeTypeDefinition> getOperationalAttributeTypes() 1607 { 1608 return operationalATSet; 1609 } 1610 1611 1612 1613 /** 1614 * Retrieves the set of user attribute type definitions (i.e., those 1615 * definitions with a usage of userApplications) contained in the server 1616 * schema. 1617 * 1618 * @return The set of user attribute type definitions contained in the server 1619 * schema. 1620 */ 1621 public Set<AttributeTypeDefinition> getUserAttributeTypes() 1622 { 1623 return userATSet; 1624 } 1625 1626 1627 1628 /** 1629 * Retrieves the attribute type with the specified name or OID from the server 1630 * schema. 1631 * 1632 * @param name The name or OID of the attribute type to retrieve. It must 1633 * not be {@code null}. 1634 * 1635 * @return The requested attribute type, or {@code null} if there is no 1636 * such attribute type defined in the server schema. 1637 */ 1638 public AttributeTypeDefinition getAttributeType(final String name) 1639 { 1640 Validator.ensureNotNull(name); 1641 1642 return atMap.get(StaticUtils.toLowerCase(name)); 1643 } 1644 1645 1646 1647 /** 1648 * Retrieves a list of all subordinate attribute type definitions for the 1649 * provided attribute type definition. 1650 * 1651 * @param d The attribute type definition for which to retrieve all 1652 * subordinate attribute types. It must not be {@code null}. 1653 * 1654 * @return A list of all subordinate attribute type definitions for the 1655 * provided attribute type definition, or an empty list if it does 1656 * not have any subordinate types or the provided attribute type is 1657 * not defined in the schema. 1658 */ 1659 public List<AttributeTypeDefinition> getSubordinateAttributeTypes( 1660 final AttributeTypeDefinition d) 1661 { 1662 Validator.ensureNotNull(d); 1663 1664 final List<AttributeTypeDefinition> l = subordinateAttributeTypes.get(d); 1665 if (l == null) 1666 { 1667 return Collections.emptyList(); 1668 } 1669 else 1670 { 1671 return Collections.unmodifiableList(l); 1672 } 1673 } 1674 1675 1676 1677 /** 1678 * Retrieves the set of DIT content rule definitions contained in the server 1679 * schema. 1680 * 1681 * @return The set of DIT content rule definitions contained in the server 1682 * schema. 1683 */ 1684 public Set<DITContentRuleDefinition> getDITContentRules() 1685 { 1686 return dcrSet; 1687 } 1688 1689 1690 1691 /** 1692 * Retrieves the DIT content rule with the specified name or OID from the 1693 * server schema. 1694 * 1695 * @param name The name or OID of the DIT content rule to retrieve. It must 1696 * not be {@code null}. 1697 * 1698 * @return The requested DIT content rule, or {@code null} if there is no 1699 * such rule defined in the server schema. 1700 */ 1701 public DITContentRuleDefinition getDITContentRule(final String name) 1702 { 1703 Validator.ensureNotNull(name); 1704 1705 return dcrMap.get(StaticUtils.toLowerCase(name)); 1706 } 1707 1708 1709 1710 /** 1711 * Retrieves the set of DIT structure rule definitions contained in the server 1712 * schema. 1713 * 1714 * @return The set of DIT structure rule definitions contained in the server 1715 * schema. 1716 */ 1717 public Set<DITStructureRuleDefinition> getDITStructureRules() 1718 { 1719 return dsrSet; 1720 } 1721 1722 1723 1724 /** 1725 * Retrieves the DIT content rule with the specified rule ID from the server 1726 * schema. 1727 * 1728 * @param ruleID The rule ID for the DIT structure rule to retrieve. 1729 * 1730 * @return The requested DIT structure rule, or {@code null} if there is no 1731 * such rule defined in the server schema. 1732 */ 1733 public DITStructureRuleDefinition getDITStructureRuleByID(final int ruleID) 1734 { 1735 return dsrMapByID.get(ruleID); 1736 } 1737 1738 1739 1740 /** 1741 * Retrieves the DIT content rule with the specified name from the server 1742 * schema. 1743 * 1744 * @param ruleName The name of the DIT structure rule to retrieve. It must 1745 * not be {@code null}. 1746 * 1747 * @return The requested DIT structure rule, or {@code null} if there is no 1748 * such rule defined in the server schema. 1749 */ 1750 public DITStructureRuleDefinition getDITStructureRuleByName( 1751 final String ruleName) 1752 { 1753 Validator.ensureNotNull(ruleName); 1754 1755 return dsrMapByName.get(StaticUtils.toLowerCase(ruleName)); 1756 } 1757 1758 1759 1760 /** 1761 * Retrieves the DIT content rule associated with the specified name form from 1762 * the server schema. 1763 * 1764 * @param nameForm The name or OID of the name form for which to retrieve 1765 * the associated DIT structure rule. 1766 * 1767 * @return The requested DIT structure rule, or {@code null} if there is no 1768 * such rule defined in the server schema. 1769 */ 1770 public DITStructureRuleDefinition getDITStructureRuleByNameForm( 1771 final String nameForm) 1772 { 1773 Validator.ensureNotNull(nameForm); 1774 1775 return dsrMapByNameForm.get(StaticUtils.toLowerCase(nameForm)); 1776 } 1777 1778 1779 1780 /** 1781 * Retrieves the set of matching rule definitions contained in the server 1782 * schema. 1783 * 1784 * @return The set of matching rule definitions contained in the server 1785 * schema. 1786 */ 1787 public Set<MatchingRuleDefinition> getMatchingRules() 1788 { 1789 return mrSet; 1790 } 1791 1792 1793 1794 /** 1795 * Retrieves the matching rule with the specified name or OID from the server 1796 * schema. 1797 * 1798 * @param name The name or OID of the matching rule to retrieve. It must 1799 * not be {@code null}. 1800 * 1801 * @return The requested matching rule, or {@code null} if there is no 1802 * such rule defined in the server schema. 1803 */ 1804 public MatchingRuleDefinition getMatchingRule(final String name) 1805 { 1806 Validator.ensureNotNull(name); 1807 1808 return mrMap.get(StaticUtils.toLowerCase(name)); 1809 } 1810 1811 1812 1813 /** 1814 * Retrieves the set of matching rule use definitions contained in the server 1815 * schema. 1816 * 1817 * @return The set of matching rule use definitions contained in the server 1818 * schema. 1819 */ 1820 public Set<MatchingRuleUseDefinition> getMatchingRuleUses() 1821 { 1822 return mruSet; 1823 } 1824 1825 1826 1827 /** 1828 * Retrieves the matching rule use with the specified name or OID from the 1829 * server schema. 1830 * 1831 * @param name The name or OID of the matching rule use to retrieve. It 1832 * must not be {@code null}. 1833 * 1834 * @return The requested matching rule, or {@code null} if there is no 1835 * such matching rule use defined in the server schema. 1836 */ 1837 public MatchingRuleUseDefinition getMatchingRuleUse(final String name) 1838 { 1839 Validator.ensureNotNull(name); 1840 1841 return mruMap.get(StaticUtils.toLowerCase(name)); 1842 } 1843 1844 1845 1846 /** 1847 * Retrieves the set of name form definitions contained in the server schema. 1848 * 1849 * @return The set of name form definitions contained in the server schema. 1850 */ 1851 public Set<NameFormDefinition> getNameForms() 1852 { 1853 return nfSet; 1854 } 1855 1856 1857 1858 /** 1859 * Retrieves the name form with the specified name or OID from the server 1860 * schema. 1861 * 1862 * @param name The name or OID of the name form to retrieve. It must not be 1863 * {@code null}. 1864 * 1865 * @return The requested name form, or {@code null} if there is no 1866 * such rule defined in the server schema. 1867 */ 1868 public NameFormDefinition getNameFormByName(final String name) 1869 { 1870 Validator.ensureNotNull(name); 1871 1872 return nfMapByName.get(StaticUtils.toLowerCase(name)); 1873 } 1874 1875 1876 1877 /** 1878 * Retrieves the name form associated with the specified structural object 1879 * class from the server schema. 1880 * 1881 * @param objectClass The name or OID of the structural object class for 1882 * which to retrieve the associated name form. It must 1883 * not be {@code null}. 1884 * 1885 * @return The requested name form, or {@code null} if there is no 1886 * such rule defined in the server schema. 1887 */ 1888 public NameFormDefinition getNameFormByObjectClass(final String objectClass) 1889 { 1890 Validator.ensureNotNull(objectClass); 1891 1892 return nfMapByOC.get(StaticUtils.toLowerCase(objectClass)); 1893 } 1894 1895 1896 1897 /** 1898 * Retrieves the set of object class definitions contained in the server 1899 * schema. 1900 * 1901 * @return The set of object class definitions contained in the server 1902 * schema. 1903 */ 1904 public Set<ObjectClassDefinition> getObjectClasses() 1905 { 1906 return ocSet; 1907 } 1908 1909 1910 1911 /** 1912 * Retrieves the set of abstract object class definitions contained in the 1913 * server schema. 1914 * 1915 * @return The set of abstract object class definitions contained in the 1916 * server schema. 1917 */ 1918 public Set<ObjectClassDefinition> getAbstractObjectClasses() 1919 { 1920 return abstractOCSet; 1921 } 1922 1923 1924 1925 /** 1926 * Retrieves the set of auxiliary object class definitions contained in the 1927 * server schema. 1928 * 1929 * @return The set of auxiliary object class definitions contained in the 1930 * server schema. 1931 */ 1932 public Set<ObjectClassDefinition> getAuxiliaryObjectClasses() 1933 { 1934 return auxiliaryOCSet; 1935 } 1936 1937 1938 1939 /** 1940 * Retrieves the set of structural object class definitions contained in the 1941 * server schema. 1942 * 1943 * @return The set of structural object class definitions contained in the 1944 * server schema. 1945 */ 1946 public Set<ObjectClassDefinition> getStructuralObjectClasses() 1947 { 1948 return structuralOCSet; 1949 } 1950 1951 1952 1953 /** 1954 * Retrieves the object class with the specified name or OID from the server 1955 * schema. 1956 * 1957 * @param name The name or OID of the object class to retrieve. It must 1958 * not be {@code null}. 1959 * 1960 * @return The requested object class, or {@code null} if there is no such 1961 * class defined in the server schema. 1962 */ 1963 public ObjectClassDefinition getObjectClass(final String name) 1964 { 1965 Validator.ensureNotNull(name); 1966 1967 return ocMap.get(StaticUtils.toLowerCase(name)); 1968 } 1969 1970 1971 1972 /** 1973 * Retrieves a hash code for this schema object. 1974 * 1975 * @return A hash code for this schema object. 1976 */ 1977 @Override() 1978 public int hashCode() 1979 { 1980 int hc; 1981 try 1982 { 1983 hc = schemaEntry.getParsedDN().hashCode(); 1984 } 1985 catch (final Exception e) 1986 { 1987 Debug.debugException(e); 1988 hc = StaticUtils.toLowerCase(schemaEntry.getDN()).hashCode(); 1989 } 1990 1991 Attribute a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_SYNTAX); 1992 if (a != null) 1993 { 1994 hc += a.hashCode(); 1995 } 1996 1997 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE); 1998 if (a != null) 1999 { 2000 hc += a.hashCode(); 2001 } 2002 2003 a = schemaEntry.getAttribute(ATTR_ATTRIBUTE_TYPE); 2004 if (a != null) 2005 { 2006 hc += a.hashCode(); 2007 } 2008 2009 a = schemaEntry.getAttribute(ATTR_OBJECT_CLASS); 2010 if (a != null) 2011 { 2012 hc += a.hashCode(); 2013 } 2014 2015 a = schemaEntry.getAttribute(ATTR_NAME_FORM); 2016 if (a != null) 2017 { 2018 hc += a.hashCode(); 2019 } 2020 2021 a = schemaEntry.getAttribute(ATTR_DIT_CONTENT_RULE); 2022 if (a != null) 2023 { 2024 hc += a.hashCode(); 2025 } 2026 2027 a = schemaEntry.getAttribute(ATTR_DIT_STRUCTURE_RULE); 2028 if (a != null) 2029 { 2030 hc += a.hashCode(); 2031 } 2032 2033 a = schemaEntry.getAttribute(ATTR_MATCHING_RULE_USE); 2034 if (a != null) 2035 { 2036 hc += a.hashCode(); 2037 } 2038 2039 return hc; 2040 } 2041 2042 2043 2044 /** 2045 * Indicates whether the provided object is equal to this schema object. 2046 * 2047 * @param o The object for which to make the determination. 2048 * 2049 * @return {@code true} if the provided object is equal to this schema 2050 * object, or {@code false} if not. 2051 */ 2052 @Override() 2053 public boolean equals(final Object o) 2054 { 2055 if (o == null) 2056 { 2057 return false; 2058 } 2059 2060 if (o == this) 2061 { 2062 return true; 2063 } 2064 2065 if (! (o instanceof Schema)) 2066 { 2067 return false; 2068 } 2069 2070 final Schema s = (Schema) o; 2071 2072 try 2073 { 2074 if (! schemaEntry.getParsedDN().equals(s.schemaEntry.getParsedDN())) 2075 { 2076 return false; 2077 } 2078 } 2079 catch (final Exception e) 2080 { 2081 Debug.debugException(e); 2082 if (! schemaEntry.getDN().equalsIgnoreCase(s.schemaEntry.getDN())) 2083 { 2084 return false; 2085 } 2086 } 2087 2088 return (asSet.equals(s.asSet) && 2089 mrSet.equals(s.mrSet) && 2090 atSet.equals(s.atSet) && 2091 ocSet.equals(s.ocSet) && 2092 nfSet.equals(s.nfSet) && 2093 dcrSet.equals(s.dcrSet) && 2094 dsrSet.equals(s.dsrSet) && 2095 mruSet.equals(s.mruSet)); 2096 } 2097 2098 2099 2100 /** 2101 * Retrieves a string representation of the associated schema entry. 2102 * 2103 * @return A string representation of the associated schema entry. 2104 */ 2105 @Override() 2106 public String toString() 2107 { 2108 return schemaEntry.toString(); 2109 } 2110}