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