001/* 002 * CDDL HEADER START 003 * 004 * The contents of this file are subject to the terms of the 005 * Common Development and Distribution License, Version 1.0 only 006 * (the "License"). You may not use this file except in compliance 007 * with the License. 008 * 009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt 010 * or http://forgerock.org/license/CDDLv1.0.html. 011 * See the License for the specific language governing permissions 012 * and limitations under the License. 013 * 014 * When distributing Covered Code, include this CDDL HEADER in each 015 * file and include the License file at legal-notices/CDDLv1_0.txt. 016 * If applicable, add the following below this CDDL HEADER, with the 017 * fields enclosed by brackets "[]" replaced with your own identifying 018 * information: 019 * Portions Copyright [yyyy] [name of copyright owner] 020 * 021 * CDDL HEADER END 022 * 023 * 024 * Copyright 2009-2010 Sun Microsystems, Inc. 025 * Portions Copyright 2011-2015 ForgeRock AS 026 * Portions Copyright 2014 Manuel Gaupp 027 */ 028package org.forgerock.opendj.ldap.schema; 029 030import java.util.Collection; 031import java.util.Collections; 032import java.util.LinkedList; 033import java.util.List; 034import java.util.Map; 035 036import org.forgerock.i18n.LocalizableMessage; 037import org.forgerock.opendj.ldap.AVA; 038import org.forgerock.opendj.ldap.Attribute; 039import org.forgerock.opendj.ldap.AttributeDescription; 040import org.forgerock.opendj.ldap.Attributes; 041import org.forgerock.opendj.ldap.ByteString; 042import org.forgerock.opendj.ldap.Connection; 043import org.forgerock.opendj.ldap.DN; 044import org.forgerock.opendj.ldap.Entries; 045import org.forgerock.opendj.ldap.Entry; 046import org.forgerock.opendj.ldap.EntryNotFoundException; 047import org.forgerock.opendj.ldap.LdapException; 048import org.forgerock.opendj.ldap.LdapPromise; 049import org.forgerock.opendj.ldap.LinkedAttribute; 050import org.forgerock.opendj.ldap.Option; 051import org.forgerock.opendj.ldap.RDN; 052import org.forgerock.util.Reject; 053import org.forgerock.util.Function; 054 055import com.forgerock.opendj.util.StaticUtils; 056 057import static org.forgerock.opendj.ldap.AttributeDescription.*; 058import static com.forgerock.opendj.ldap.CoreMessages.*; 059 060/** 061 * This class defines a data structure that holds information about the 062 * components of the LDAP schema. It includes the following kinds of elements: 063 * <UL> 064 * <LI>Attribute type definitions</LI> 065 * <LI>Object class definitions</LI> 066 * <LI>Attribute syntax definitions</LI> 067 * <LI>Matching rule definitions</LI> 068 * <LI>Matching rule use definitions</LI> 069 * <LI>DIT content rule definitions</LI> 070 * <LI>DIT structure rule definitions</LI> 071 * <LI>Name form definitions</LI> 072 * </UL> 073 */ 074public final class Schema { 075 private static interface Impl { 076 Schema asNonStrictSchema(); 077 078 Schema asStrictSchema(); 079 080 SchemaOptions getOptions(); 081 082 MatchingRule getDefaultMatchingRule(); 083 084 Syntax getDefaultSyntax(); 085 086 String getOIDForName(String lowerCaseName); 087 088 AttributeType getAttributeType(Schema schema, String name); 089 090 Collection<AttributeType> getAttributeTypes(); 091 092 List<AttributeType> getAttributeTypesWithName(String name); 093 094 DITContentRule getDITContentRule(ObjectClass structuralClass); 095 096 DITContentRule getDITContentRule(String name); 097 098 Collection<DITContentRule> getDITContentRules(); 099 100 Collection<DITContentRule> getDITContentRulesWithName(String name); 101 102 DITStructureRule getDITStructureRule(int ruleID); 103 104 Collection<DITStructureRule> getDITStructureRules(NameForm nameForm); 105 106 Collection<DITStructureRule> getDITStructureRulesWithName(String name); 107 108 Collection<DITStructureRule> getDITStuctureRules(); 109 110 MatchingRule getMatchingRule(String name); 111 112 Collection<MatchingRule> getMatchingRules(); 113 114 Collection<MatchingRule> getMatchingRulesWithName(String name); 115 116 MatchingRuleUse getMatchingRuleUse(MatchingRule matchingRule); 117 118 MatchingRuleUse getMatchingRuleUse(String name); 119 120 Collection<MatchingRuleUse> getMatchingRuleUses(); 121 122 Collection<MatchingRuleUse> getMatchingRuleUsesWithName(String name); 123 124 NameForm getNameForm(String name); 125 126 Collection<NameForm> getNameForms(); 127 128 Collection<NameForm> getNameForms(ObjectClass structuralClass); 129 130 Collection<NameForm> getNameFormsWithName(String name); 131 132 ObjectClass getObjectClass(String name); 133 134 Collection<ObjectClass> getObjectClasses(); 135 136 Collection<ObjectClass> getObjectClassesWithName(String name); 137 138 String getSchemaName(); 139 140 Syntax getSyntax(Schema schema, String numericOID); 141 142 Collection<Syntax> getSyntaxes(); 143 144 Collection<LocalizableMessage> getWarnings(); 145 146 boolean hasAttributeType(String name); 147 148 boolean hasDITContentRule(String name); 149 150 boolean hasDITStructureRule(int ruleID); 151 152 boolean hasMatchingRule(String name); 153 154 boolean hasMatchingRuleUse(String name); 155 156 boolean hasNameForm(String name); 157 158 boolean hasObjectClass(String name); 159 160 boolean hasSyntax(String numericOID); 161 162 boolean isStrict(); 163 } 164 165 private static final class NonStrictImpl implements Impl { 166 private final StrictImpl strictImpl; 167 168 private NonStrictImpl(final StrictImpl strictImpl) { 169 this.strictImpl = strictImpl; 170 } 171 172 @Override 173 public Schema asNonStrictSchema() { 174 return strictImpl.asNonStrictSchema(); 175 } 176 177 @Override 178 public Schema asStrictSchema() { 179 return strictImpl.asStrictSchema(); 180 } 181 182 @Override 183 public SchemaOptions getOptions() { 184 return strictImpl.getOptions(); 185 } 186 187 @Override 188 public Syntax getDefaultSyntax() { 189 return strictImpl.getDefaultSyntax(); 190 } 191 192 @Override 193 public MatchingRule getDefaultMatchingRule() { 194 return strictImpl.getDefaultMatchingRule(); 195 } 196 197 @Override 198 public String getOIDForName(final String lowerCaseName) { 199 return strictImpl.getOIDForName(lowerCaseName); 200 } 201 202 @Override 203 public AttributeType getAttributeType(final Schema schema, final String name) { 204 final AttributeType type = strictImpl.getAttributeType0(name); 205 return type != null ? type : new AttributeType(schema, name); 206 } 207 208 @Override 209 public Collection<AttributeType> getAttributeTypes() { 210 return strictImpl.getAttributeTypes(); 211 } 212 213 @Override 214 public List<AttributeType> getAttributeTypesWithName(final String name) { 215 return strictImpl.getAttributeTypesWithName(name); 216 } 217 218 @Override 219 public DITContentRule getDITContentRule(final ObjectClass structuralClass) { 220 return strictImpl.getDITContentRule(structuralClass); 221 } 222 223 @Override 224 public DITContentRule getDITContentRule(final String name) { 225 return strictImpl.getDITContentRule(name); 226 } 227 228 @Override 229 public Collection<DITContentRule> getDITContentRules() { 230 return strictImpl.getDITContentRules(); 231 } 232 233 @Override 234 public Collection<DITContentRule> getDITContentRulesWithName(final String name) { 235 return strictImpl.getDITContentRulesWithName(name); 236 } 237 238 @Override 239 public DITStructureRule getDITStructureRule(final int ruleID) { 240 return strictImpl.getDITStructureRule(ruleID); 241 } 242 243 @Override 244 public Collection<DITStructureRule> getDITStructureRules(final NameForm nameForm) { 245 return strictImpl.getDITStructureRules(nameForm); 246 } 247 248 @Override 249 public Collection<DITStructureRule> getDITStructureRulesWithName(final String name) { 250 return strictImpl.getDITStructureRulesWithName(name); 251 } 252 253 @Override 254 public Collection<DITStructureRule> getDITStuctureRules() { 255 return strictImpl.getDITStuctureRules(); 256 } 257 258 @Override 259 public MatchingRule getMatchingRule(final String name) { 260 return strictImpl.getMatchingRule(name); 261 } 262 263 @Override 264 public Collection<MatchingRule> getMatchingRules() { 265 return strictImpl.getMatchingRules(); 266 } 267 268 @Override 269 public Collection<MatchingRule> getMatchingRulesWithName(final String name) { 270 return strictImpl.getMatchingRulesWithName(name); 271 } 272 273 @Override 274 public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) { 275 return strictImpl.getMatchingRuleUse(matchingRule); 276 } 277 278 @Override 279 public MatchingRuleUse getMatchingRuleUse(final String name) { 280 return strictImpl.getMatchingRuleUse(name); 281 } 282 283 @Override 284 public Collection<MatchingRuleUse> getMatchingRuleUses() { 285 return strictImpl.getMatchingRuleUses(); 286 } 287 288 @Override 289 public Collection<MatchingRuleUse> getMatchingRuleUsesWithName(final String name) { 290 return strictImpl.getMatchingRuleUsesWithName(name); 291 } 292 293 @Override 294 public NameForm getNameForm(final String name) { 295 return strictImpl.getNameForm(name); 296 } 297 298 @Override 299 public Collection<NameForm> getNameForms() { 300 return strictImpl.getNameForms(); 301 } 302 303 @Override 304 public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { 305 return strictImpl.getNameForms(structuralClass); 306 } 307 308 @Override 309 public Collection<NameForm> getNameFormsWithName(final String name) { 310 return strictImpl.getNameFormsWithName(name); 311 } 312 313 @Override 314 public ObjectClass getObjectClass(final String name) { 315 return strictImpl.getObjectClass(name); 316 } 317 318 @Override 319 public Collection<ObjectClass> getObjectClasses() { 320 return strictImpl.getObjectClasses(); 321 } 322 323 @Override 324 public Collection<ObjectClass> getObjectClassesWithName(final String name) { 325 return strictImpl.getObjectClassesWithName(name); 326 } 327 328 @Override 329 public String getSchemaName() { 330 return strictImpl.getSchemaName(); 331 } 332 333 @Override 334 public Syntax getSyntax(final Schema schema, final String numericOID) { 335 if (!strictImpl.hasSyntax(numericOID)) { 336 return new Syntax(schema, numericOID); 337 } 338 return strictImpl.getSyntax(schema, numericOID); 339 } 340 341 @Override 342 public Collection<Syntax> getSyntaxes() { 343 return strictImpl.getSyntaxes(); 344 } 345 346 @Override 347 public Collection<LocalizableMessage> getWarnings() { 348 return strictImpl.getWarnings(); 349 } 350 351 @Override 352 public boolean hasAttributeType(final String name) { 353 // In theory a non-strict schema always contains the requested 354 // attribute type, so we could always return true. However, we 355 // should provide a way for callers to differentiate between a 356 // real attribute type and a faked up attribute type. 357 return strictImpl.hasAttributeType(name); 358 } 359 360 @Override 361 public boolean hasDITContentRule(final String name) { 362 return strictImpl.hasDITContentRule(name); 363 } 364 365 @Override 366 public boolean hasDITStructureRule(final int ruleID) { 367 return strictImpl.hasDITStructureRule(ruleID); 368 } 369 370 @Override 371 public boolean hasMatchingRule(final String name) { 372 return strictImpl.hasMatchingRule(name); 373 } 374 375 @Override 376 public boolean hasMatchingRuleUse(final String name) { 377 return strictImpl.hasMatchingRuleUse(name); 378 } 379 380 @Override 381 public boolean hasNameForm(final String name) { 382 return strictImpl.hasNameForm(name); 383 } 384 385 @Override 386 public boolean hasObjectClass(final String name) { 387 return strictImpl.hasObjectClass(name); 388 } 389 390 @Override 391 public boolean hasSyntax(final String numericOID) { 392 return strictImpl.hasSyntax(numericOID); 393 } 394 395 @Override 396 public boolean isStrict() { 397 return false; 398 } 399 } 400 401 static final class StrictImpl implements Impl { 402 private final Map<Integer, DITStructureRule> id2StructureRules; 403 private final Map<String, List<AttributeType>> name2AttributeTypes; 404 private final Map<String, List<DITContentRule>> name2ContentRules; 405 private final Map<String, List<MatchingRule>> name2MatchingRules; 406 private final Map<String, List<MatchingRuleUse>> name2MatchingRuleUses; 407 private final Map<String, List<NameForm>> name2NameForms; 408 private final Map<String, List<ObjectClass>> name2ObjectClasses; 409 private final Map<String, List<DITStructureRule>> name2StructureRules; 410 private final Map<String, List<DITStructureRule>> nameForm2StructureRules; 411 private final Map<String, AttributeType> numericOID2AttributeTypes; 412 private final Map<String, DITContentRule> numericOID2ContentRules; 413 private final Map<String, MatchingRule> numericOID2MatchingRules; 414 private final Map<String, MatchingRuleUse> numericOID2MatchingRuleUses; 415 private final Map<String, NameForm> numericOID2NameForms; 416 private final Map<String, ObjectClass> numericOID2ObjectClasses; 417 private final Map<String, Syntax> numericOID2Syntaxes; 418 private final Map<String, List<NameForm>> objectClass2NameForms; 419 private final Map<String, String> name2OIDs; 420 private final List<LocalizableMessage> warnings; 421 private final String schemaName; 422 private final SchemaOptions options; 423 private final Syntax defaultSyntax; 424 private final MatchingRule defaultMatchingRule; 425 private final Schema strictSchema; 426 private final Schema nonStrictSchema; 427 428 StrictImpl(final String schemaName, 429 final SchemaOptions options, 430 final Syntax defaultSyntax, 431 final MatchingRule defaultMatchingRule, 432 final Map<String, Syntax> numericOID2Syntaxes, 433 final Map<String, MatchingRule> numericOID2MatchingRules, 434 final Map<String, MatchingRuleUse> numericOID2MatchingRuleUses, 435 final Map<String, AttributeType> numericOID2AttributeTypes, 436 final Map<String, ObjectClass> numericOID2ObjectClasses, 437 final Map<String, NameForm> numericOID2NameForms, 438 final Map<String, DITContentRule> numericOID2ContentRules, 439 final Map<Integer, DITStructureRule> id2StructureRules, 440 final Map<String, List<MatchingRule>> name2MatchingRules, 441 final Map<String, List<MatchingRuleUse>> name2MatchingRuleUses, 442 final Map<String, List<AttributeType>> name2AttributeTypes, 443 final Map<String, List<ObjectClass>> name2ObjectClasses, 444 final Map<String, List<NameForm>> name2NameForms, 445 final Map<String, List<DITContentRule>> name2ContentRules, 446 final Map<String, List<DITStructureRule>> name2StructureRules, 447 final Map<String, List<NameForm>> objectClass2NameForms, 448 final Map<String, List<DITStructureRule>> nameForm2StructureRules, 449 final Map<String, String> name2OIDs, 450 final List<LocalizableMessage> warnings) { 451 this.schemaName = schemaName; 452 this.options = SchemaOptions.unmodifiable(options); 453 this.defaultSyntax = defaultSyntax; 454 this.defaultMatchingRule = defaultMatchingRule; 455 this.numericOID2Syntaxes = Collections.unmodifiableMap(numericOID2Syntaxes); 456 this.numericOID2MatchingRules = Collections.unmodifiableMap(numericOID2MatchingRules); 457 this.numericOID2MatchingRuleUses = Collections.unmodifiableMap(numericOID2MatchingRuleUses); 458 this.numericOID2AttributeTypes = Collections.unmodifiableMap(numericOID2AttributeTypes); 459 this.numericOID2ObjectClasses = Collections.unmodifiableMap(numericOID2ObjectClasses); 460 this.numericOID2NameForms = Collections.unmodifiableMap(numericOID2NameForms); 461 this.numericOID2ContentRules = Collections.unmodifiableMap(numericOID2ContentRules); 462 this.id2StructureRules = Collections.unmodifiableMap(id2StructureRules); 463 this.name2MatchingRules = Collections.unmodifiableMap(name2MatchingRules); 464 this.name2MatchingRuleUses = Collections.unmodifiableMap(name2MatchingRuleUses); 465 this.name2AttributeTypes = Collections.unmodifiableMap(name2AttributeTypes); 466 this.name2ObjectClasses = Collections.unmodifiableMap(name2ObjectClasses); 467 this.name2NameForms = Collections.unmodifiableMap(name2NameForms); 468 this.name2ContentRules = Collections.unmodifiableMap(name2ContentRules); 469 this.name2StructureRules = Collections.unmodifiableMap(name2StructureRules); 470 this.objectClass2NameForms = Collections.unmodifiableMap(objectClass2NameForms); 471 this.nameForm2StructureRules = Collections.unmodifiableMap(nameForm2StructureRules); 472 this.name2OIDs = Collections.unmodifiableMap(name2OIDs); 473 this.warnings = Collections.unmodifiableList(warnings); 474 this.strictSchema = new Schema(this); 475 this.nonStrictSchema = new Schema(new NonStrictImpl(this)); 476 } 477 478 @Override 479 public Schema asNonStrictSchema() { 480 return nonStrictSchema; 481 } 482 483 @Override 484 public Schema asStrictSchema() { 485 return strictSchema; 486 } 487 488 @Override 489 public SchemaOptions getOptions() { 490 return options; 491 } 492 493 @Override 494 public Syntax getDefaultSyntax() { 495 return defaultSyntax; 496 } 497 498 @Override 499 public MatchingRule getDefaultMatchingRule() { 500 return defaultMatchingRule; 501 } 502 503 @Override 504 public String getOIDForName(String lowerCaseName) { 505 final String oid = name2OIDs.get(lowerCaseName); 506 // == is correct, AMBIGUOUS_OID is singleton to mark an entry ambiguous 507 if (oid == SchemaBuilder.AMBIGUOUS_OID) { 508 throw new UnknownSchemaElementException(WARN_NAME_AMBIGUOUS.get(lowerCaseName)); 509 } 510 return oid; 511 } 512 513 @Override 514 public AttributeType getAttributeType(final Schema schema, final String name) { 515 final AttributeType type = getAttributeType0(name); 516 if (type != null) { 517 return type; 518 } else { 519 throw new UnknownSchemaElementException(WARN_ATTR_TYPE_UNKNOWN.get(name)); 520 } 521 } 522 523 @Override 524 public Collection<AttributeType> getAttributeTypes() { 525 return numericOID2AttributeTypes.values(); 526 } 527 528 @Override 529 public List<AttributeType> getAttributeTypesWithName(final String name) { 530 final List<AttributeType> attributes = 531 name2AttributeTypes.get(StaticUtils.toLowerCase(name)); 532 if (attributes != null) { 533 return attributes; 534 } 535 return Collections.emptyList(); 536 } 537 538 @Override 539 public DITContentRule getDITContentRule(final ObjectClass structuralClass) { 540 return numericOID2ContentRules.get(structuralClass.getOID()); 541 } 542 543 @Override 544 public DITContentRule getDITContentRule(final String name) { 545 final DITContentRule rule = numericOID2ContentRules.get(name); 546 if (rule != null) { 547 return rule; 548 } 549 final List<DITContentRule> rules = name2ContentRules.get(StaticUtils.toLowerCase(name)); 550 if (rules != null) { 551 if (rules.size() == 1) { 552 return rules.get(0); 553 } 554 throw new UnknownSchemaElementException(WARN_DCR_AMBIGUOUS.get(name)); 555 } 556 throw new UnknownSchemaElementException(WARN_DCR_UNKNOWN.get(name)); 557 } 558 559 @Override 560 public Collection<DITContentRule> getDITContentRules() { 561 return numericOID2ContentRules.values(); 562 } 563 564 @Override 565 public Collection<DITContentRule> getDITContentRulesWithName(final String name) { 566 final List<DITContentRule> rules = name2ContentRules.get(StaticUtils.toLowerCase(name)); 567 if (rules != null) { 568 return rules; 569 } 570 return Collections.emptyList(); 571 } 572 573 @Override 574 public DITStructureRule getDITStructureRule(final int ruleID) { 575 final DITStructureRule rule = id2StructureRules.get(ruleID); 576 if (rule == null) { 577 throw new UnknownSchemaElementException(WARN_DSR_UNKNOWN 578 .get(String.valueOf(ruleID))); 579 } 580 return rule; 581 } 582 583 @Override 584 public Collection<DITStructureRule> getDITStructureRules(final NameForm nameForm) { 585 final List<DITStructureRule> rules = nameForm2StructureRules.get(nameForm.getOID()); 586 if (rules != null) { 587 return rules; 588 } 589 return Collections.emptyList(); 590 } 591 592 @Override 593 public Collection<DITStructureRule> getDITStructureRulesWithName(final String name) { 594 final List<DITStructureRule> rules = 595 name2StructureRules.get(StaticUtils.toLowerCase(name)); 596 if (rules != null) { 597 return rules; 598 } 599 return Collections.emptyList(); 600 } 601 602 @Override 603 public Collection<DITStructureRule> getDITStuctureRules() { 604 return id2StructureRules.values(); 605 } 606 607 @Override 608 public MatchingRule getMatchingRule(final String name) { 609 final MatchingRule rule = numericOID2MatchingRules.get(name); 610 if (rule != null) { 611 return rule; 612 } 613 final List<MatchingRule> rules = name2MatchingRules.get(StaticUtils.toLowerCase(name)); 614 if (rules != null) { 615 if (rules.size() == 1) { 616 return rules.get(0); 617 } 618 throw new UnknownSchemaElementException(WARN_MR_AMBIGUOUS.get(name)); 619 } 620 throw new UnknownSchemaElementException(WARN_MR_UNKNOWN.get(name)); 621 } 622 623 @Override 624 public Collection<MatchingRule> getMatchingRules() { 625 return numericOID2MatchingRules.values(); 626 } 627 628 @Override 629 public Collection<MatchingRule> getMatchingRulesWithName(final String name) { 630 final List<MatchingRule> rules = name2MatchingRules.get(StaticUtils.toLowerCase(name)); 631 if (rules != null) { 632 return rules; 633 } 634 return Collections.emptyList(); 635 } 636 637 @Override 638 public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) { 639 return numericOID2MatchingRuleUses.get(matchingRule.getOID()); 640 } 641 642 @Override 643 public MatchingRuleUse getMatchingRuleUse(final String name) { 644 final MatchingRuleUse rule = numericOID2MatchingRuleUses.get(name); 645 if (rule != null) { 646 return rule; 647 } 648 final List<MatchingRuleUse> uses = 649 name2MatchingRuleUses.get(StaticUtils.toLowerCase(name)); 650 if (uses != null) { 651 if (uses.size() == 1) { 652 return uses.get(0); 653 } 654 throw new UnknownSchemaElementException(WARN_MRU_AMBIGUOUS.get(name)); 655 } 656 throw new UnknownSchemaElementException(WARN_MRU_UNKNOWN.get(name)); 657 } 658 659 @Override 660 public Collection<MatchingRuleUse> getMatchingRuleUses() { 661 return numericOID2MatchingRuleUses.values(); 662 } 663 664 @Override 665 public Collection<MatchingRuleUse> getMatchingRuleUsesWithName(final String name) { 666 final List<MatchingRuleUse> rules = 667 name2MatchingRuleUses.get(StaticUtils.toLowerCase(name)); 668 if (rules != null) { 669 return rules; 670 } 671 return Collections.emptyList(); 672 } 673 674 @Override 675 public NameForm getNameForm(final String name) { 676 final NameForm form = numericOID2NameForms.get(name); 677 if (form != null) { 678 return form; 679 } 680 final List<NameForm> forms = name2NameForms.get(StaticUtils.toLowerCase(name)); 681 if (forms != null) { 682 if (forms.size() == 1) { 683 return forms.get(0); 684 } 685 throw new UnknownSchemaElementException(WARN_NAMEFORM_AMBIGUOUS.get(name)); 686 } 687 throw new UnknownSchemaElementException(WARN_NAMEFORM_UNKNOWN.get(name)); 688 } 689 690 @Override 691 public Collection<NameForm> getNameForms() { 692 return numericOID2NameForms.values(); 693 } 694 695 @Override 696 public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { 697 final List<NameForm> forms = objectClass2NameForms.get(structuralClass.getOID()); 698 if (forms != null) { 699 return forms; 700 } 701 return Collections.emptyList(); 702 } 703 704 @Override 705 public Collection<NameForm> getNameFormsWithName(final String name) { 706 final List<NameForm> forms = name2NameForms.get(StaticUtils.toLowerCase(name)); 707 if (forms != null) { 708 return forms; 709 } 710 return Collections.emptyList(); 711 } 712 713 @Override 714 public ObjectClass getObjectClass(final String name) { 715 final ObjectClass oc = numericOID2ObjectClasses.get(name); 716 if (oc != null) { 717 return oc; 718 } 719 final List<ObjectClass> classes = name2ObjectClasses.get(StaticUtils.toLowerCase(name)); 720 if (classes != null) { 721 if (classes.size() == 1) { 722 return classes.get(0); 723 } 724 throw new UnknownSchemaElementException(WARN_OBJECTCLASS_AMBIGUOUS.get(name)); 725 } 726 throw new UnknownSchemaElementException(WARN_OBJECTCLASS_UNKNOWN.get(name)); 727 } 728 729 @Override 730 public Collection<ObjectClass> getObjectClasses() { 731 return numericOID2ObjectClasses.values(); 732 } 733 734 @Override 735 public Collection<ObjectClass> getObjectClassesWithName(final String name) { 736 final List<ObjectClass> classes = name2ObjectClasses.get(StaticUtils.toLowerCase(name)); 737 if (classes != null) { 738 return classes; 739 } 740 return Collections.emptyList(); 741 } 742 743 @Override 744 public String getSchemaName() { 745 return schemaName; 746 } 747 748 @Override 749 public Syntax getSyntax(final Schema schema, final String numericOID) { 750 final Syntax syntax = numericOID2Syntaxes.get(numericOID); 751 if (syntax == null) { 752 throw new UnknownSchemaElementException(WARN_SYNTAX_UNKNOWN.get(numericOID)); 753 } 754 return syntax; 755 } 756 757 @Override 758 public Collection<Syntax> getSyntaxes() { 759 return numericOID2Syntaxes.values(); 760 } 761 762 @Override 763 public Collection<LocalizableMessage> getWarnings() { 764 return warnings; 765 } 766 767 @Override 768 public boolean hasAttributeType(final String name) { 769 if (numericOID2AttributeTypes.containsKey(name)) { 770 return true; 771 } 772 final List<AttributeType> attributes = 773 name2AttributeTypes.get(StaticUtils.toLowerCase(name)); 774 return attributes != null && attributes.size() == 1; 775 } 776 777 @Override 778 public boolean hasDITContentRule(final String name) { 779 if (numericOID2ContentRules.containsKey(name)) { 780 return true; 781 } 782 final List<DITContentRule> rules = name2ContentRules.get(StaticUtils.toLowerCase(name)); 783 return rules != null && rules.size() == 1; 784 } 785 786 @Override 787 public boolean hasDITStructureRule(final int ruleID) { 788 return id2StructureRules.containsKey(ruleID); 789 } 790 791 @Override 792 public boolean hasMatchingRule(final String name) { 793 if (numericOID2MatchingRules.containsKey(name)) { 794 return true; 795 } 796 final List<MatchingRule> rules = name2MatchingRules.get(StaticUtils.toLowerCase(name)); 797 return rules != null && rules.size() == 1; 798 } 799 800 @Override 801 public boolean hasMatchingRuleUse(final String name) { 802 if (numericOID2MatchingRuleUses.containsKey(name)) { 803 return true; 804 } 805 final List<MatchingRuleUse> uses = 806 name2MatchingRuleUses.get(StaticUtils.toLowerCase(name)); 807 return uses != null && uses.size() == 1; 808 } 809 810 @Override 811 public boolean hasNameForm(final String name) { 812 if (numericOID2NameForms.containsKey(name)) { 813 return true; 814 } 815 final List<NameForm> forms = name2NameForms.get(StaticUtils.toLowerCase(name)); 816 return forms != null && forms.size() == 1; 817 } 818 819 @Override 820 public boolean hasObjectClass(final String name) { 821 if (numericOID2ObjectClasses.containsKey(name)) { 822 return true; 823 } 824 final List<ObjectClass> classes = name2ObjectClasses.get(StaticUtils.toLowerCase(name)); 825 return classes != null && classes.size() == 1; 826 } 827 828 @Override 829 public boolean hasSyntax(final String numericOID) { 830 return numericOID2Syntaxes.containsKey(numericOID); 831 } 832 833 @Override 834 public boolean isStrict() { 835 return true; 836 } 837 838 AttributeType getAttributeType0(final String name) { 839 final AttributeType type = numericOID2AttributeTypes.get(name); 840 if (type != null) { 841 return type; 842 } 843 final List<AttributeType> attributes = 844 name2AttributeTypes.get(StaticUtils.toLowerCase(name)); 845 if (attributes != null) { 846 if (attributes.size() == 1) { 847 return attributes.get(0); 848 } 849 throw new UnknownSchemaElementException(WARN_ATTR_TYPE_AMBIGUOUS.get(name)); 850 } 851 return null; 852 } 853 } 854 855 static final String ATTR_ATTRIBUTE_TYPES = "attributeTypes"; 856 static final String ATTR_DIT_CONTENT_RULES = "dITContentRules"; 857 static final String ATTR_DIT_STRUCTURE_RULES = "dITStructureRules"; 858 static final String ATTR_LDAP_SYNTAXES = "ldapSyntaxes"; 859 static final String ATTR_MATCHING_RULE_USE = "matchingRuleUse"; 860 static final String ATTR_MATCHING_RULES = "matchingRules"; 861 static final String ATTR_NAME_FORMS = "nameForms"; 862 static final String ATTR_OBJECT_CLASSES = "objectClasses"; 863 864 /** 865 * Returns the core schema. The core schema is non-strict and contains the 866 * following standard LDAP schema elements: 867 * <ul> 868 * <li><a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight 869 * Directory Access Protocol (LDAP): Directory Information Models </a> 870 * <li><a href="http://tools.ietf.org/html/rfc4517">RFC 4517 - Lightweight 871 * Directory Access Protocol (LDAP): Syntaxes and Matching Rules </a> 872 * <li><a href="http://tools.ietf.org/html/rfc4519">RFC 4519 - Lightweight 873 * Directory Access Protocol (LDAP): Schema for User Applications </a> 874 * <li><a href="http://tools.ietf.org/html/rfc4530">RFC 4530 - Lightweight 875 * Directory Access Protocol (LDAP): entryUUID Operational Attribute </a> 876 * <li><a href="http://tools.ietf.org/html/rfc3045">RFC 3045 - Storing 877 * Vendor Information in the LDAP root DSE </a> 878 * <li><a href="http://tools.ietf.org/html/rfc3112">RFC 3112 - LDAP 879 * Authentication Password Schema </a> 880 * </ul> 881 * 882 * @return The core schema. 883 */ 884 public static Schema getCoreSchema() { 885 return CoreSchemaImpl.getInstance(); 886 } 887 888 /** 889 * Returns the default schema which should be used by this application. The 890 * default schema is initially set to the core schema. 891 * 892 * @return The default schema which should be used by this application. 893 */ 894 public static Schema getDefaultSchema() { 895 return DelayedSchema.defaultSchema; 896 } 897 898 /** 899 * Returns the empty schema. The empty schema is non-strict and does not 900 * contain any schema elements. 901 * 902 * @return The empty schema. 903 */ 904 public static Schema getEmptySchema() { 905 return DelayedSchema.EMPTY_SCHEMA; 906 } 907 908 /** 909 * Reads the schema contained in the named subschema sub-entry. 910 * <p> 911 * If the requested schema is not returned by the Directory Server then the 912 * request will fail with an {@link EntryNotFoundException}. More 913 * specifically, this method will never return {@code null}. 914 * 915 * @param connection 916 * A connection to the Directory Server whose schema is to be 917 * read. 918 * @param name 919 * The distinguished name of the subschema sub-entry. 920 * @return The schema from the Directory Server. 921 * @throws LdapException 922 * If the result code indicates that the request failed for some 923 * reason. 924 * @throws UnsupportedOperationException 925 * If the connection does not support search operations. 926 * @throws IllegalStateException 927 * If the connection has already been closed, i.e. if 928 * {@code connection.isClosed() == true}. 929 * @throws NullPointerException 930 * If the {@code connection} or {@code name} was {@code null}. 931 */ 932 public static Schema readSchema(final Connection connection, final DN name) throws LdapException { 933 return new SchemaBuilder().addSchema(connection, name, true).toSchema(); 934 } 935 936 /** 937 * Asynchronously reads the schema contained in the named subschema 938 * sub-entry. 939 * <p> 940 * If the requested schema is not returned by the Directory Server then the 941 * request will fail with an {@link EntryNotFoundException}. More 942 * specifically, the returned promise will never return {@code null}. 943 * 944 * @param connection 945 * A connection to the Directory Server whose schema is to be 946 * read. 947 * @param name 948 * The distinguished name of the subschema sub-entry. 949 * the operation result when it is received, may be {@code null}. 950 * @return A promise representing the retrieved schema. 951 * @throws UnsupportedOperationException 952 * If the connection does not support search operations. 953 * @throws IllegalStateException 954 * If the connection has already been closed, i.e. if 955 * {@code connection.isClosed() == true}. 956 * @throws NullPointerException 957 * If the {@code connection} or {@code name} was {@code null}. 958 */ 959 public static LdapPromise<Schema> readSchemaAsync(final Connection connection, final DN name) { 960 final SchemaBuilder builder = new SchemaBuilder(); 961 return builder.addSchemaAsync(connection, name, true).then( 962 new Function<SchemaBuilder, Schema, LdapException>() { 963 @Override 964 public Schema apply(SchemaBuilder builder) throws LdapException { 965 return builder.toSchema(); 966 } 967 }); 968 } 969 970 /** 971 * Reads the schema contained in the subschema sub-entry which applies to 972 * the named entry. 973 * <p> 974 * If the requested entry or its associated schema are not returned by the 975 * Directory Server then the request will fail with an 976 * {@link EntryNotFoundException}. More specifically, this method will never 977 * return {@code null}. 978 * <p> 979 * This implementation first reads the {@code subschemaSubentry} attribute 980 * of the entry in order to identify the schema and then invokes 981 * {@link #readSchema(Connection, DN)} to read the schema. 982 * 983 * @param connection 984 * A connection to the Directory Server whose schema is to be 985 * read. 986 * @param name 987 * The distinguished name of the entry whose schema is to be 988 * located. 989 * @return The schema from the Directory Server which applies to the named 990 * entry. 991 * @throws LdapException 992 * If the result code indicates that the request failed for some 993 * reason. 994 * @throws UnsupportedOperationException 995 * If the connection does not support search operations. 996 * @throws IllegalStateException 997 * If the connection has already been closed, i.e. if 998 * {@code connection.isClosed() == true}. 999 * @throws NullPointerException 1000 * If the {@code connection} or {@code name} was {@code null}. 1001 */ 1002 public static Schema readSchemaForEntry(final Connection connection, final DN name) 1003 throws LdapException { 1004 return new SchemaBuilder().addSchemaForEntry(connection, name, true).toSchema(); 1005 } 1006 1007 /** 1008 * Asynchronously reads the schema contained in the subschema sub-entry 1009 * which applies to the named entry. 1010 * <p> 1011 * If the requested entry or its associated schema are not returned by the 1012 * Directory Server then the request will fail with an 1013 * {@link EntryNotFoundException}. More specifically, the returned promise 1014 * will never return {@code null}. 1015 * <p> 1016 * This implementation first reads the {@code subschemaSubentry} attribute 1017 * of the entry in order to identify the schema and then invokes 1018 * {@link #readSchemaAsync(Connection, DN, ResultHandler)} to read the 1019 * schema. 1020 * 1021 * @param connection 1022 * A connection to the Directory Server whose schema is to be 1023 * read. 1024 * @param name 1025 * The distinguished name of the entry whose schema is to be 1026 * located. 1027 * @return A promise representing the retrieved schema. 1028 * @throws UnsupportedOperationException 1029 * If the connection does not support search operations. 1030 * @throws IllegalStateException 1031 * If the connection has already been closed, i.e. if 1032 * {@code connection.isClosed() == true}. 1033 * @throws NullPointerException 1034 * If the {@code connection} or {@code name} was {@code null}. 1035 */ 1036 public static LdapPromise<Schema> readSchemaForEntryAsync(final Connection connection, final DN name) { 1037 final SchemaBuilder builder = new SchemaBuilder(); 1038 return builder.addSchemaForEntryAsync(connection, name, true).then( 1039 new Function<SchemaBuilder, Schema, LdapException>() { 1040 @Override 1041 public Schema apply(SchemaBuilder builder) throws LdapException { 1042 return builder.toSchema(); 1043 } 1044 }); 1045 } 1046 1047 /** 1048 * Sets the default schema which should be used by this application. The 1049 * default schema is initially set to the core schema. 1050 * 1051 * @param schema 1052 * The default schema which should be used by this application. 1053 */ 1054 public static void setDefaultSchema(final Schema schema) { 1055 Reject.ifNull(schema); 1056 DelayedSchema.defaultSchema = schema; 1057 } 1058 1059 /** 1060 * Parses the provided entry as a subschema subentry. Any problems 1061 * encountered while parsing the entry can be retrieved using the returned 1062 * schema's {@link #getWarnings()} method. 1063 * 1064 * @param entry 1065 * The subschema subentry to be parsed. 1066 * @return The parsed schema. 1067 */ 1068 public static Schema valueOf(final Entry entry) { 1069 return new SchemaBuilder(entry).toSchema(); 1070 } 1071 1072 private final Impl impl; 1073 1074 Schema(final Impl impl) { 1075 this.impl = impl; 1076 } 1077 1078 /** 1079 * Returns a non-strict view of this schema. 1080 * <p> 1081 * See the description of {@link #isStrict()} for more details. 1082 * 1083 * @return A non-strict view of this schema. 1084 * @see Schema#isStrict() 1085 */ 1086 public Schema asNonStrictSchema() { 1087 return impl.asNonStrictSchema(); 1088 } 1089 1090 /** 1091 * Returns a strict view of this schema. 1092 * <p> 1093 * See the description of {@link #isStrict()} for more details. 1094 * 1095 * @return A strict view of this schema. 1096 * @see Schema#isStrict() 1097 */ 1098 public Schema asStrictSchema() { 1099 return impl.asStrictSchema(); 1100 } 1101 1102 MatchingRule getDefaultMatchingRule() { 1103 return impl.getDefaultMatchingRule(); 1104 } 1105 1106 Syntax getDefaultSyntax() { 1107 return impl.getDefaultSyntax(); 1108 } 1109 1110 /** 1111 * Return the numerical OID matching the lowerCaseName. 1112 * @param lowerCaseName The lower case name 1113 * @return OID matching the name or null if name doesn't match to an OID 1114 * @throws UnknownSchemaElementException if multiple OID are matching 1115 * lowerCaseName 1116 */ 1117 String getOIDForName(String lowerCaseName) { 1118 return impl.getOIDForName(lowerCaseName); 1119 } 1120 1121 /** 1122 * Returns the attribute type with the specified name or numeric OID. 1123 * <p> 1124 * If the requested attribute type is not registered in this schema and this 1125 * schema is non-strict then a temporary "place-holder" attribute type will 1126 * be created and returned. Place holder attribute types have an OID which 1127 * is the normalized attribute name with the string {@code -oid} appended. 1128 * In addition, they will use the directory string syntax and case ignore 1129 * matching rule. 1130 * 1131 * @param name 1132 * The name or OID of the attribute type to retrieve. 1133 * @return The requested attribute type. 1134 * @throws UnknownSchemaElementException 1135 * If this is a strict schema and the requested attribute type 1136 * was not found or if the provided name is ambiguous. 1137 * @see AttributeType#isPlaceHolder() 1138 */ 1139 public AttributeType getAttributeType(final String name) { 1140 return impl.getAttributeType(this, name); 1141 } 1142 1143 /** 1144 * Returns an unmodifiable collection containing all of the attribute types 1145 * contained in this schema. 1146 * 1147 * @return An unmodifiable collection containing all of the attribute types 1148 * contained in this schema. 1149 */ 1150 public Collection<AttributeType> getAttributeTypes() { 1151 return impl.getAttributeTypes(); 1152 } 1153 1154 /** 1155 * Returns an unmodifiable collection containing all of the attribute types 1156 * having the specified name or numeric OID. 1157 * 1158 * @param name 1159 * The name or OID of the attribute types to retrieve. 1160 * @return An unmodifiable collection containing all of the attribute types 1161 * having the specified name or numeric OID. 1162 */ 1163 public List<AttributeType> getAttributeTypesWithName(final String name) { 1164 return impl.getAttributeTypesWithName(name); 1165 } 1166 1167 /** 1168 * Returns the DIT content rule associated with the provided structural 1169 * object class, or {@code null} if no rule is defined. 1170 * 1171 * @param structuralClass 1172 * The structural object class . 1173 * @return The DIT content rule associated with the provided structural 1174 * object class, or {@code null} if no rule is defined. 1175 */ 1176 public DITContentRule getDITContentRule(final ObjectClass structuralClass) { 1177 return impl.getDITContentRule(structuralClass); 1178 } 1179 1180 /** 1181 * Returns the DIT content rule with the specified name or numeric OID. 1182 * 1183 * @param name 1184 * The name or OID of the DIT content rule to retrieve. 1185 * @return The requested DIT content rule. 1186 * @throws UnknownSchemaElementException 1187 * If this is a strict schema and the requested DIT content rule 1188 * was not found or if the provided name is ambiguous. 1189 */ 1190 public DITContentRule getDITContentRule(final String name) { 1191 return impl.getDITContentRule(name); 1192 } 1193 1194 /** 1195 * Returns an unmodifiable collection containing all of the DIT content 1196 * rules contained in this schema. 1197 * 1198 * @return An unmodifiable collection containing all of the DIT content 1199 * rules contained in this schema. 1200 */ 1201 public Collection<DITContentRule> getDITContentRules() { 1202 return impl.getDITContentRules(); 1203 } 1204 1205 /** 1206 * Returns an unmodifiable collection containing all of the DIT content 1207 * rules having the specified name or numeric OID. 1208 * 1209 * @param name 1210 * The name or OID of the DIT content rules to retrieve. 1211 * @return An unmodifiable collection containing all of the DIT content 1212 * rules having the specified name or numeric OID. 1213 */ 1214 public Collection<DITContentRule> getDITContentRulesWithName(final String name) { 1215 return impl.getDITContentRulesWithName(name); 1216 } 1217 1218 /** 1219 * Returns the DIT structure rule with the specified name or numeric OID. 1220 * 1221 * @param ruleID 1222 * The ID of the DIT structure rule to retrieve. 1223 * @return The requested DIT structure rule. 1224 * @throws UnknownSchemaElementException 1225 * If this is a strict schema and the requested DIT structure 1226 * rule was not found. 1227 */ 1228 public DITStructureRule getDITStructureRule(final int ruleID) { 1229 return impl.getDITStructureRule(ruleID); 1230 } 1231 1232 /** 1233 * Returns an unmodifiable collection containing all of the DIT structure 1234 * rules associated with the provided name form. 1235 * 1236 * @param nameForm 1237 * The name form. 1238 * @return An unmodifiable collection containing all of the DIT structure 1239 * rules associated with the provided name form. 1240 */ 1241 public Collection<DITStructureRule> getDITStructureRules(final NameForm nameForm) { 1242 return impl.getDITStructureRules(nameForm); 1243 } 1244 1245 /** 1246 * Returns an unmodifiable collection containing all of the DIT structure 1247 * rules having the specified name or numeric OID. 1248 * 1249 * @param name 1250 * The name or OID of the DIT structure rules to retrieve. 1251 * @return An unmodifiable collection containing all of the DIT structure 1252 * rules having the specified name or numeric OID. 1253 */ 1254 public Collection<DITStructureRule> getDITStructureRulesWithName(final String name) { 1255 return impl.getDITStructureRulesWithName(name); 1256 } 1257 1258 /** 1259 * Returns an unmodifiable collection containing all of the DIT structure 1260 * rules contained in this schema. 1261 * 1262 * @return An unmodifiable collection containing all of the DIT structure 1263 * rules contained in this schema. 1264 */ 1265 public Collection<DITStructureRule> getDITStuctureRules() { 1266 return impl.getDITStuctureRules(); 1267 } 1268 1269 /** 1270 * Returns the matching rule with the specified name or numeric OID. 1271 * 1272 * @param name 1273 * The name or OID of the matching rule to retrieve. 1274 * @return The requested matching rule. 1275 * @throws UnknownSchemaElementException 1276 * If this is a strict schema and the requested matching rule 1277 * was not found or if the provided name is ambiguous. 1278 */ 1279 public MatchingRule getMatchingRule(final String name) { 1280 return impl.getMatchingRule(name); 1281 } 1282 1283 /** 1284 * Returns an unmodifiable collection containing all of the matching rules 1285 * contained in this schema. 1286 * 1287 * @return An unmodifiable collection containing all of the matching rules 1288 * contained in this schema. 1289 */ 1290 public Collection<MatchingRule> getMatchingRules() { 1291 return impl.getMatchingRules(); 1292 } 1293 1294 /** 1295 * Returns an unmodifiable collection containing all of the matching rules 1296 * having the specified name or numeric OID. 1297 * 1298 * @param name 1299 * The name or OID of the matching rules to retrieve. 1300 * @return An unmodifiable collection containing all of the matching rules 1301 * having the specified name or numeric OID. 1302 */ 1303 public Collection<MatchingRule> getMatchingRulesWithName(final String name) { 1304 return impl.getMatchingRulesWithName(name); 1305 } 1306 1307 /** 1308 * Returns the matching rule use associated with the provided matching rule, 1309 * or {@code null} if no use is defined. 1310 * 1311 * @param matchingRule 1312 * The matching rule whose matching rule use is to be retrieved. 1313 * @return The matching rule use associated with the provided matching rule, 1314 * or {@code null} if no use is defined. 1315 */ 1316 public MatchingRuleUse getMatchingRuleUse(final MatchingRule matchingRule) { 1317 return getMatchingRuleUse(matchingRule.getOID()); 1318 } 1319 1320 /** 1321 * Returns the matching rule use with the specified name or numeric OID. 1322 * 1323 * @param name 1324 * The name or OID of the matching rule use to retrieve. 1325 * @return The requested matching rule use. 1326 * @throws UnknownSchemaElementException 1327 * If this is a strict schema and the requested matching rule 1328 * use was not found or if the provided name is ambiguous. 1329 */ 1330 public MatchingRuleUse getMatchingRuleUse(final String name) { 1331 return impl.getMatchingRuleUse(name); 1332 } 1333 1334 /** 1335 * Returns an unmodifiable collection containing all of the matching rule 1336 * uses contained in this schema. 1337 * 1338 * @return An unmodifiable collection containing all of the matching rule 1339 * uses contained in this schema. 1340 */ 1341 public Collection<MatchingRuleUse> getMatchingRuleUses() { 1342 return impl.getMatchingRuleUses(); 1343 } 1344 1345 /** 1346 * Returns an unmodifiable collection containing all of the matching rule 1347 * uses having the specified name or numeric OID. 1348 * 1349 * @param name 1350 * The name or OID of the matching rule uses to retrieve. 1351 * @return An unmodifiable collection containing all of the matching rule 1352 * uses having the specified name or numeric OID. 1353 */ 1354 public Collection<MatchingRuleUse> getMatchingRuleUsesWithName(final String name) { 1355 return impl.getMatchingRuleUsesWithName(name); 1356 } 1357 1358 /** 1359 * Returns the name form with the specified name or numeric OID. 1360 * 1361 * @param name 1362 * The name or OID of the name form to retrieve. 1363 * @return The requested name form. 1364 * @throws UnknownSchemaElementException 1365 * If this is a strict schema and the requested name form was 1366 * not found or if the provided name is ambiguous. 1367 */ 1368 public NameForm getNameForm(final String name) { 1369 return impl.getNameForm(name); 1370 } 1371 1372 /** 1373 * Returns an unmodifiable collection containing all of the name forms 1374 * contained in this schema. 1375 * 1376 * @return An unmodifiable collection containing all of the name forms 1377 * contained in this schema. 1378 */ 1379 public Collection<NameForm> getNameForms() { 1380 return impl.getNameForms(); 1381 } 1382 1383 /** 1384 * Returns an unmodifiable collection containing all of the name forms 1385 * associated with the provided structural object class. 1386 * 1387 * @param structuralClass 1388 * The structural object class whose name forms are to be 1389 * retrieved. 1390 * @return An unmodifiable collection containing all of the name forms 1391 * associated with the provided structural object class. 1392 */ 1393 public Collection<NameForm> getNameForms(final ObjectClass structuralClass) { 1394 return impl.getNameForms(structuralClass); 1395 } 1396 1397 /** 1398 * Returns an unmodifiable collection containing all of the name forms 1399 * having the specified name or numeric OID. 1400 * 1401 * @param name 1402 * The name or OID of the name forms to retrieve. 1403 * @return An unmodifiable collection containing all of the name forms 1404 * having the specified name or numeric OID. 1405 */ 1406 public Collection<NameForm> getNameFormsWithName(final String name) { 1407 return impl.getNameFormsWithName(name); 1408 } 1409 1410 /** 1411 * Returns the object class with the specified name or numeric OID. 1412 * 1413 * @param name 1414 * The name or OID of the object class to retrieve. 1415 * @return The requested object class. 1416 * @throws UnknownSchemaElementException 1417 * If this is a strict schema and the requested object class was 1418 * not found or if the provided name is ambiguous. 1419 */ 1420 public ObjectClass getObjectClass(final String name) { 1421 return impl.getObjectClass(name); 1422 } 1423 1424 /** 1425 * Returns an unmodifiable collection containing all of the object classes 1426 * contained in this schema. 1427 * 1428 * @return An unmodifiable collection containing all of the object classes 1429 * contained in this schema. 1430 */ 1431 public Collection<ObjectClass> getObjectClasses() { 1432 return impl.getObjectClasses(); 1433 } 1434 1435 /** 1436 * Returns an unmodifiable collection containing all of the object classes 1437 * having the specified name or numeric OID. 1438 * 1439 * @param name 1440 * The name or OID of the object classes to retrieve. 1441 * @return An unmodifiable collection containing all of the object classes 1442 * having the specified name or numeric OID. 1443 */ 1444 public Collection<ObjectClass> getObjectClassesWithName(final String name) { 1445 return impl.getObjectClassesWithName(name); 1446 } 1447 1448 /** 1449 * Returns the value associated to the provided {@link Option} or the option 1450 * default value, if there is no such option in this schema. 1451 * 1452 * @param <T> 1453 * The option type. 1454 * @param option 1455 * The option whose associated value should to be retrieve. 1456 * @return The value associated to the provided {@link Option} or the option 1457 * default value, if there is no such option in this schema. 1458 */ 1459 public <T> T getOption(Option<T> option) { 1460 return getOptions().get(option); 1461 } 1462 1463 SchemaOptions getOptions() { 1464 return impl.getOptions(); 1465 } 1466 1467 /** 1468 * Returns the user-friendly name of this schema which may be used for 1469 * debugging purposes. The format of the schema name is not defined but 1470 * should contain the distinguished name of the subschema sub-entry for 1471 * those schemas retrieved from a Directory Server. 1472 * 1473 * @return The user-friendly name of this schema which may be used for 1474 * debugging purposes. 1475 */ 1476 public String getSchemaName() { 1477 return impl.getSchemaName(); 1478 } 1479 1480 /** 1481 * Returns the syntax with the specified numeric OID. 1482 * 1483 * @param numericOID 1484 * The OID of the syntax to retrieve. 1485 * @return The requested syntax. 1486 * @throws UnknownSchemaElementException 1487 * If this is a strict schema and the requested syntax was not 1488 * found or if the provided name is ambiguous. 1489 */ 1490 public Syntax getSyntax(final String numericOID) { 1491 return impl.getSyntax(this, numericOID); 1492 } 1493 1494 /** 1495 * Returns an unmodifiable collection containing all of the syntaxes 1496 * contained in this schema. 1497 * 1498 * @return An unmodifiable collection containing all of the syntaxes 1499 * contained in this schema. 1500 */ 1501 public Collection<Syntax> getSyntaxes() { 1502 return impl.getSyntaxes(); 1503 } 1504 1505 /** 1506 * Returns an unmodifiable collection containing all of the warnings that 1507 * were detected when this schema was constructed. 1508 * 1509 * @return An unmodifiable collection containing all of the warnings that 1510 * were detected when this schema was constructed. 1511 */ 1512 public Collection<LocalizableMessage> getWarnings() { 1513 return impl.getWarnings(); 1514 } 1515 1516 /** 1517 * Indicates whether or not this schema contains an attribute type with the 1518 * specified name or numeric OID. 1519 * 1520 * @param name 1521 * The name or OID of the attribute type. 1522 * @return {@code true} if this schema contains an attribute type with the 1523 * specified name or numeric OID, otherwise {@code false}. 1524 */ 1525 public boolean hasAttributeType(final String name) { 1526 return impl.hasAttributeType(name); 1527 } 1528 1529 /** 1530 * Indicates whether or not this schema contains a DIT content rule with the 1531 * specified name or numeric OID. 1532 * 1533 * @param name 1534 * The name or OID of the DIT content rule. 1535 * @return {@code true} if this schema contains a DIT content rule with the 1536 * specified name or numeric OID, otherwise {@code false}. 1537 */ 1538 public boolean hasDITContentRule(final String name) { 1539 return impl.hasDITContentRule(name); 1540 } 1541 1542 /** 1543 * Indicates whether or not this schema contains a DIT structure rule with 1544 * the specified rule ID. 1545 * 1546 * @param ruleID 1547 * The ID of the DIT structure rule. 1548 * @return {@code true} if this schema contains a DIT structure rule with 1549 * the specified rule ID, otherwise {@code false}. 1550 */ 1551 public boolean hasDITStructureRule(final int ruleID) { 1552 return impl.hasDITStructureRule(ruleID); 1553 } 1554 1555 /** 1556 * Indicates whether or not this schema contains a matching rule with the 1557 * specified name or numeric OID. 1558 * 1559 * @param name 1560 * The name or OID of the matching rule. 1561 * @return {@code true} if this schema contains a matching rule with the 1562 * specified name or numeric OID, otherwise {@code false}. 1563 */ 1564 public boolean hasMatchingRule(final String name) { 1565 return impl.hasMatchingRule(name); 1566 } 1567 1568 /** 1569 * Indicates whether or not this schema contains a matching rule use with 1570 * the specified name or numeric OID. 1571 * 1572 * @param name 1573 * The name or OID of the matching rule use. 1574 * @return {@code true} if this schema contains a matching rule use with the 1575 * specified name or numeric OID, otherwise {@code false}. 1576 */ 1577 public boolean hasMatchingRuleUse(final String name) { 1578 return impl.hasMatchingRuleUse(name); 1579 } 1580 1581 /** 1582 * Indicates whether or not this schema contains a name form with the 1583 * specified name or numeric OID. 1584 * 1585 * @param name 1586 * The name or OID of the name form. 1587 * @return {@code true} if this schema contains a name form with the 1588 * specified name or numeric OID, otherwise {@code false}. 1589 */ 1590 public boolean hasNameForm(final String name) { 1591 return impl.hasNameForm(name); 1592 } 1593 1594 /** 1595 * Indicates whether or not this schema contains an object class with the 1596 * specified name or numeric OID. 1597 * 1598 * @param name 1599 * The name or OID of the object class. 1600 * @return {@code true} if this schema contains an object class with the 1601 * specified name or numeric OID, otherwise {@code false}. 1602 */ 1603 public boolean hasObjectClass(final String name) { 1604 return impl.hasObjectClass(name); 1605 } 1606 1607 /** 1608 * Indicates whether or not this schema contains a syntax with the specified 1609 * numeric OID. 1610 * 1611 * @param numericOID 1612 * The OID of the syntax. 1613 * @return {@code true} if this schema contains a syntax with the specified 1614 * numeric OID, otherwise {@code false}. 1615 */ 1616 public boolean hasSyntax(final String numericOID) { 1617 return impl.hasSyntax(numericOID); 1618 } 1619 1620 /** 1621 * Indicates whether or not this schema is strict. 1622 * <p> 1623 * Attribute type queries against non-strict schema always succeed: if the 1624 * requested attribute type is not found then a temporary attribute type is 1625 * created automatically having the Octet String syntax and associated 1626 * matching rules. 1627 * <p> 1628 * Strict schema, on the other hand, throw an 1629 * {@link UnknownSchemaElementException} whenever an attempt is made to 1630 * retrieve a non-existent attribute type. 1631 * 1632 * @return {@code true} if this schema is strict. 1633 */ 1634 public boolean isStrict() { 1635 return impl.isStrict(); 1636 } 1637 1638 /** 1639 * Adds the definitions of all the schema elements contained in this schema 1640 * to the provided subschema subentry. Any existing attributes (including 1641 * schema definitions) contained in the provided entry will be preserved. 1642 * 1643 * @param entry 1644 * The subschema subentry to which all schema definitions should 1645 * be added. 1646 * @return The updated subschema subentry. 1647 * @throws NullPointerException 1648 * If {@code entry} was {@code null}. 1649 */ 1650 public Entry toEntry(final Entry entry) { 1651 Attribute attr = new LinkedAttribute(Schema.ATTR_LDAP_SYNTAXES); 1652 for (final Syntax syntax : getSyntaxes()) { 1653 attr.add(syntax.toString()); 1654 } 1655 if (!attr.isEmpty()) { 1656 entry.addAttribute(attr); 1657 } 1658 1659 attr = new LinkedAttribute(Schema.ATTR_ATTRIBUTE_TYPES); 1660 for (final AttributeType attributeType : getAttributeTypes()) { 1661 attr.add(attributeType.toString()); 1662 } 1663 if (!attr.isEmpty()) { 1664 entry.addAttribute(attr); 1665 } 1666 1667 attr = new LinkedAttribute(Schema.ATTR_OBJECT_CLASSES); 1668 for (final ObjectClass objectClass : getObjectClasses()) { 1669 attr.add(objectClass.toString()); 1670 } 1671 if (!attr.isEmpty()) { 1672 entry.addAttribute(attr); 1673 } 1674 1675 attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULE_USE); 1676 for (final MatchingRuleUse matchingRuleUse : getMatchingRuleUses()) { 1677 attr.add(matchingRuleUse.toString()); 1678 } 1679 if (!attr.isEmpty()) { 1680 entry.addAttribute(attr); 1681 } 1682 1683 attr = new LinkedAttribute(Schema.ATTR_MATCHING_RULES); 1684 for (final MatchingRule matchingRule : getMatchingRules()) { 1685 attr.add(matchingRule.toString()); 1686 } 1687 if (!attr.isEmpty()) { 1688 entry.addAttribute(attr); 1689 } 1690 1691 attr = new LinkedAttribute(Schema.ATTR_DIT_CONTENT_RULES); 1692 for (final DITContentRule ditContentRule : getDITContentRules()) { 1693 attr.add(ditContentRule.toString()); 1694 } 1695 if (!attr.isEmpty()) { 1696 entry.addAttribute(attr); 1697 } 1698 1699 attr = new LinkedAttribute(Schema.ATTR_DIT_STRUCTURE_RULES); 1700 for (final DITStructureRule ditStructureRule : getDITStuctureRules()) { 1701 attr.add(ditStructureRule.toString()); 1702 } 1703 if (!attr.isEmpty()) { 1704 entry.addAttribute(attr); 1705 } 1706 1707 attr = new LinkedAttribute(Schema.ATTR_NAME_FORMS); 1708 for (final NameForm nameForm : getNameForms()) { 1709 attr.add(nameForm.toString()); 1710 } 1711 if (!attr.isEmpty()) { 1712 entry.addAttribute(attr); 1713 } 1714 1715 return entry; 1716 } 1717 1718 /** 1719 * Returns {@code true} if the provided entry is valid according to this 1720 * schema and the specified schema validation policy. 1721 * <p> 1722 * If attribute value validation is enabled then following checks will be 1723 * performed: 1724 * <ul> 1725 * <li>checking that there is at least one value 1726 * <li>checking that single-valued attributes contain only a single value 1727 * </ul> 1728 * In particular, attribute values will not be checked for conformance to 1729 * their syntax since this is expected to have already been performed while 1730 * adding the values to the entry. 1731 * 1732 * @param entry 1733 * The entry to be validated. 1734 * @param policy 1735 * The schema validation policy. 1736 * @param errorMessages 1737 * A collection into which any schema validation warnings or 1738 * error messages can be placed, or {@code null} if they should 1739 * not be saved. 1740 * @return {@code true} if an entry conforms to this schema based on the 1741 * provided schema validation policy. 1742 */ 1743 public boolean validateEntry(final Entry entry, final SchemaValidationPolicy policy, 1744 final Collection<LocalizableMessage> errorMessages) { 1745 // First check that the object classes are recognized and that there is 1746 // one structural object class. 1747 ObjectClass structuralObjectClass = null; 1748 final Attribute objectClassAttribute = entry.getAttribute(objectClass()); 1749 final List<ObjectClass> objectClasses = new LinkedList<>(); 1750 if (objectClassAttribute != null) { 1751 for (final ByteString v : objectClassAttribute) { 1752 final String objectClassName = v.toString(); 1753 final ObjectClass objectClass; 1754 try { 1755 objectClass = getObjectClass(objectClassName); 1756 objectClasses.add(objectClass); 1757 } catch (final UnknownSchemaElementException e) { 1758 if (policy.checkAttributesAndObjectClasses().needsChecking()) { 1759 if (errorMessages != null) { 1760 errorMessages.add(ERR_ENTRY_SCHEMA_UNKNOWN_OBJECT_CLASS.get( 1761 entry.getName(), objectClassName)); 1762 } 1763 if (policy.checkAttributesAndObjectClasses().isReject()) { 1764 return false; 1765 } 1766 } 1767 continue; 1768 } 1769 1770 if (objectClass.getObjectClassType() == ObjectClassType.STRUCTURAL) { 1771 if (structuralObjectClass == null 1772 || objectClass.isDescendantOf(structuralObjectClass)) { 1773 structuralObjectClass = objectClass; 1774 } else if (!structuralObjectClass.isDescendantOf(objectClass) 1775 && policy.requireSingleStructuralObjectClass().needsChecking()) { 1776 if (errorMessages != null) { 1777 errorMessages.add(ERR_ENTRY_SCHEMA_MULTIPLE_STRUCTURAL_CLASSES.get( 1778 entry.getName(), structuralObjectClass.getNameOrOID(), objectClassName)); 1779 } 1780 if (policy.requireSingleStructuralObjectClass().isReject()) { 1781 return false; 1782 } 1783 } 1784 } 1785 } 1786 } 1787 1788 Collection<DITStructureRule> ditStructureRules = Collections.emptyList(); 1789 DITContentRule ditContentRule = null; 1790 1791 if (structuralObjectClass == null) { 1792 if (policy.requireSingleStructuralObjectClass().needsChecking()) { 1793 if (errorMessages != null) { 1794 errorMessages.add(ERR_ENTRY_SCHEMA_NO_STRUCTURAL_CLASS.get(entry.getName())); 1795 } 1796 if (policy.requireSingleStructuralObjectClass().isReject()) { 1797 return false; 1798 } 1799 } 1800 } else { 1801 ditContentRule = getDITContentRule(structuralObjectClass); 1802 if (ditContentRule != null && ditContentRule.isObsolete()) { 1803 ditContentRule = null; 1804 } 1805 } 1806 1807 // Check entry conforms to object classes and optional content rule. 1808 if (!checkAttributesAndObjectClasses(entry, policy, errorMessages, objectClasses, 1809 ditContentRule)) { 1810 return false; 1811 } 1812 1813 // Check that the name of the entry conforms to at least one applicable 1814 // name form. 1815 if (policy.checkNameForms().needsChecking() && structuralObjectClass != null) { 1816 /** 1817 * There may be multiple name forms registered with this structural 1818 * object class. However, we need to select only one of the name 1819 * forms and its corresponding DIT structure rule(s). We will 1820 * iterate over all the name forms and see if at least one is 1821 * acceptable before rejecting the entry. DIT structure rules 1822 * corresponding to other non-acceptable name forms are not applied. 1823 */ 1824 boolean foundMatchingNameForms = false; 1825 NameForm nameForm = null; 1826 final List<LocalizableMessage> nameFormWarnings = 1827 (errorMessages != null) ? new LinkedList<LocalizableMessage>() : null; 1828 for (final NameForm nf : getNameForms(structuralObjectClass)) { 1829 if (nf.isObsolete()) { 1830 continue; 1831 } 1832 1833 // If there are any candidate name forms then at least one 1834 // should be valid. 1835 foundMatchingNameForms = true; 1836 1837 if (checkNameForm(entry, policy, nameFormWarnings, nf)) { 1838 nameForm = nf; 1839 break; 1840 } 1841 } 1842 1843 if (foundMatchingNameForms) { 1844 if (nameForm != null) { 1845 ditStructureRules = getDITStructureRules(nameForm); 1846 } else { 1847 // We couldn't match this entry against any of the name 1848 // forms, so append the reasons why they didn't match and 1849 // reject if required. 1850 if (errorMessages != null) { 1851 errorMessages.addAll(nameFormWarnings); 1852 } 1853 if (policy.checkNameForms().isReject()) { 1854 return false; 1855 } 1856 } 1857 } 1858 } 1859 1860 // Check DIT structure rules - this needs the parent entry. 1861 if (policy.checkDITStructureRules().needsChecking() && !entry.getName().isRootDN()) { 1862 boolean foundMatchingRules = false; 1863 boolean foundValidRule = false; 1864 final List<LocalizableMessage> ruleWarnings = 1865 (errorMessages != null) ? new LinkedList<LocalizableMessage>() : null; 1866 ObjectClass parentStructuralObjectClass = null; 1867 boolean parentEntryHasBeenRead = false; 1868 for (final DITStructureRule rule : ditStructureRules) { 1869 if (rule.isObsolete()) { 1870 continue; 1871 } 1872 1873 foundMatchingRules = true; 1874 1875 // A DIT structure rule with no superiors is automatically 1876 // valid, so avoid reading the parent. 1877 if (rule.getSuperiorRules().isEmpty()) { 1878 foundValidRule = true; 1879 break; 1880 } 1881 1882 if (!parentEntryHasBeenRead) { 1883 // Don't drop out immediately on failure because there may 1884 // be some 1885 // applicable rules which do not require the parent entry. 1886 parentStructuralObjectClass = 1887 getParentStructuralObjectClass(entry, policy, ruleWarnings); 1888 parentEntryHasBeenRead = true; 1889 } 1890 1891 if (parentStructuralObjectClass != null 1892 && checkDITStructureRule(entry, ruleWarnings, rule, 1893 structuralObjectClass, parentStructuralObjectClass)) { 1894 foundValidRule = true; 1895 break; 1896 } 1897 } 1898 1899 if (foundMatchingRules) { 1900 if (!foundValidRule) { 1901 // We couldn't match this entry against any of the rules, so 1902 // append the reasons why they didn't match and reject if 1903 // required. 1904 if (errorMessages != null) { 1905 errorMessages.addAll(ruleWarnings); 1906 } 1907 if (policy.checkDITStructureRules().isReject()) { 1908 return false; 1909 } 1910 } 1911 } else { 1912 // There is no DIT structure rule for this entry, but there may 1913 // be one for the parent entry. If there is such a rule for the 1914 // parent entry, then this entry will not be valid. 1915 1916 // The parent won't have been read yet. 1917 parentStructuralObjectClass = 1918 getParentStructuralObjectClass(entry, policy, ruleWarnings); 1919 if (parentStructuralObjectClass == null) { 1920 if (errorMessages != null) { 1921 errorMessages.addAll(ruleWarnings); 1922 } 1923 if (policy.checkDITStructureRules().isReject()) { 1924 return false; 1925 } 1926 } else { 1927 for (final NameForm nf : getNameForms(parentStructuralObjectClass)) { 1928 if (!nf.isObsolete()) { 1929 for (final DITStructureRule rule : getDITStructureRules(nf)) { 1930 if (!rule.isObsolete()) { 1931 if (errorMessages != null) { 1932 errorMessages.add(ERR_ENTRY_SCHEMA_DSR_MISSING_DSR.get( 1933 entry.getName(), rule.getNameOrRuleID())); 1934 } 1935 if (policy.checkDITStructureRules().isReject()) { 1936 return false; 1937 } 1938 1939 // We could break out of the loop here in 1940 // warn mode but continuing allows us to 1941 // collect all conflicts. 1942 } 1943 } 1944 } 1945 } 1946 } 1947 } 1948 } 1949 1950 // If we've gotten here, then the entry is acceptable. 1951 return true; 1952 } 1953 1954 private boolean checkAttributesAndObjectClasses(final Entry entry, 1955 final SchemaValidationPolicy policy, 1956 final Collection<LocalizableMessage> errorMessages, 1957 final List<ObjectClass> objectClasses, final DITContentRule ditContentRule) { 1958 // Check object classes. 1959 final boolean checkDITContentRule = 1960 policy.checkDITContentRules().needsChecking() && ditContentRule != null; 1961 final boolean checkObjectClasses = policy.checkAttributesAndObjectClasses().needsChecking(); 1962 final boolean checkAttributeValues = policy.checkAttributeValues().needsChecking(); 1963 1964 if (checkObjectClasses || checkDITContentRule) { 1965 for (final ObjectClass objectClass : objectClasses) { 1966 // Make sure that any auxiliary object classes are permitted by 1967 // the content rule. 1968 if (checkDITContentRule 1969 && objectClass.getObjectClassType() == ObjectClassType.AUXILIARY 1970 && !ditContentRule.getAuxiliaryClasses().contains(objectClass)) { 1971 if (errorMessages != null) { 1972 errorMessages.add(ERR_ENTRY_SCHEMA_DCR_PROHIBITED_AUXILIARY_OC.get( 1973 entry.getName(), objectClass.getNameOrOID(), ditContentRule.getNameOrOID())); 1974 } 1975 if (policy.checkDITContentRules().isReject()) { 1976 return false; 1977 } 1978 } 1979 1980 // Make sure that all of the attributes required by the object 1981 // class are present. 1982 if (checkObjectClasses) { 1983 for (final AttributeType t : objectClass.getDeclaredRequiredAttributes()) { 1984 final Attribute a = 1985 Attributes.emptyAttribute(AttributeDescription.create(t)); 1986 if (!entry.containsAttribute(a, null)) { 1987 if (errorMessages != null) { 1988 errorMessages.add(ERR_ENTRY_SCHEMA_OC_MISSING_MUST_ATTRIBUTES.get( 1989 entry.getName(), t.getNameOrOID(), objectClass.getNameOrOID())); 1990 } 1991 if (policy.checkAttributesAndObjectClasses().isReject()) { 1992 return false; 1993 } 1994 } 1995 } 1996 } 1997 } 1998 1999 // Make sure that all of the attributes required by the content rule 2000 // are present. 2001 if (checkDITContentRule) { 2002 for (final AttributeType t : ditContentRule.getRequiredAttributes()) { 2003 final Attribute a = Attributes.emptyAttribute(AttributeDescription.create(t)); 2004 if (!entry.containsAttribute(a, null)) { 2005 if (errorMessages != null) { 2006 errorMessages.add(ERR_ENTRY_SCHEMA_DCR_MISSING_MUST_ATTRIBUTES.get( 2007 entry.getName(), t.getNameOrOID(), ditContentRule.getNameOrOID())); 2008 } 2009 if (policy.checkDITContentRules().isReject()) { 2010 return false; 2011 } 2012 } 2013 } 2014 2015 // Make sure that attributes prohibited by the content rule are 2016 // not present. 2017 for (final AttributeType t : ditContentRule.getProhibitedAttributes()) { 2018 final Attribute a = Attributes.emptyAttribute(AttributeDescription.create(t)); 2019 if (entry.containsAttribute(a, null)) { 2020 if (errorMessages != null) { 2021 errorMessages.add(ERR_ENTRY_SCHEMA_DCR_PROHIBITED_ATTRIBUTES.get( 2022 entry.getName(), t.getNameOrOID(), ditContentRule.getNameOrOID())); 2023 } 2024 if (policy.checkDITContentRules().isReject()) { 2025 return false; 2026 } 2027 } 2028 } 2029 } 2030 } 2031 2032 // Check attributes. 2033 if (checkObjectClasses || checkDITContentRule || checkAttributeValues) { 2034 for (final Attribute attribute : entry.getAllAttributes()) { 2035 final AttributeType t = attribute.getAttributeDescription().getAttributeType(); 2036 2037 if (!t.isOperational() 2038 && (checkObjectClasses || checkDITContentRule)) { 2039 boolean isAllowed = false; 2040 for (final ObjectClass objectClass : objectClasses) { 2041 if (objectClass.isRequiredOrOptional(t)) { 2042 isAllowed = true; 2043 break; 2044 } 2045 } 2046 if (!isAllowed && ditContentRule != null && ditContentRule.isRequiredOrOptional(t)) { 2047 isAllowed = true; 2048 } 2049 if (!isAllowed) { 2050 if (errorMessages != null) { 2051 final LocalizableMessage message; 2052 if (ditContentRule != null) { 2053 message = ERR_ENTRY_SCHEMA_DCR_DISALLOWED_ATTRIBUTES.get( 2054 entry.getName(), t.getNameOrOID(), ditContentRule.getNameOrOID()); 2055 } else { 2056 message = ERR_ENTRY_SCHEMA_OC_DISALLOWED_ATTRIBUTES.get( 2057 entry.getName(), t.getNameOrOID()); 2058 } 2059 errorMessages.add(message); 2060 } 2061 if (policy.checkAttributesAndObjectClasses().isReject() 2062 || policy.checkDITContentRules().isReject()) { 2063 return false; 2064 } 2065 } 2066 } 2067 2068 // Check all attributes contain an appropriate number of values. 2069 if (checkAttributeValues) { 2070 final int sz = attribute.size(); 2071 2072 if (sz == 0) { 2073 if (errorMessages != null) { 2074 errorMessages.add(ERR_ENTRY_SCHEMA_AT_EMPTY_ATTRIBUTE.get( 2075 entry.getName(), t.getNameOrOID())); 2076 } 2077 if (policy.checkAttributeValues().isReject()) { 2078 return false; 2079 } 2080 } else if (sz > 1 && t.isSingleValue()) { 2081 if (errorMessages != null) { 2082 errorMessages.add(ERR_ENTRY_SCHEMA_AT_SINGLE_VALUED_ATTRIBUTE.get( 2083 entry.getName(), t.getNameOrOID())); 2084 } 2085 if (policy.checkAttributeValues().isReject()) { 2086 return false; 2087 } 2088 } 2089 } 2090 } 2091 } 2092 2093 // If we've gotten here, then things are OK. 2094 return true; 2095 } 2096 2097 private boolean checkDITStructureRule(final Entry entry, 2098 final List<LocalizableMessage> ruleWarnings, final DITStructureRule rule, 2099 final ObjectClass structuralObjectClass, final ObjectClass parentStructuralObjectClass) { 2100 boolean matchFound = false; 2101 for (final DITStructureRule parentRule : rule.getSuperiorRules()) { 2102 if (parentRule.getNameForm().getStructuralClass().equals(parentStructuralObjectClass)) { 2103 matchFound = true; 2104 } 2105 } 2106 2107 if (!matchFound) { 2108 if (ruleWarnings != null) { 2109 ruleWarnings.add(ERR_ENTRY_SCHEMA_DSR_ILLEGAL_OC.get( 2110 entry.getName(), rule.getNameOrRuleID(), structuralObjectClass.getNameOrOID(), 2111 parentStructuralObjectClass.getNameOrOID())); 2112 } 2113 return false; 2114 } 2115 2116 return true; 2117 } 2118 2119 private boolean checkNameForm(final Entry entry, final SchemaValidationPolicy policy, 2120 final List<LocalizableMessage> nameFormWarnings, final NameForm nameForm) { 2121 final RDN rdn = entry.getName().rdn(); 2122 if (rdn != null) { 2123 // Make sure that all the required AVAs are present. 2124 for (final AttributeType t : nameForm.getRequiredAttributes()) { 2125 if (rdn.getAttributeValue(t) == null) { 2126 if (nameFormWarnings != null) { 2127 nameFormWarnings.add(ERR_ENTRY_SCHEMA_NF_MISSING_MUST_ATTRIBUTES.get( 2128 entry.getName(), t.getNameOrOID(), nameForm.getNameOrOID())); 2129 } 2130 return false; 2131 } 2132 } 2133 2134 // Make sure that all AVAs in the RDN are allowed. 2135 for (final AVA ava : rdn) { 2136 final AttributeType t = ava.getAttributeType(); 2137 if (!nameForm.isRequiredOrOptional(t)) { 2138 if (nameFormWarnings != null) { 2139 nameFormWarnings.add(ERR_ENTRY_SCHEMA_NF_DISALLOWED_ATTRIBUTES.get( 2140 entry.getName(), t.getNameOrOID(), nameForm.getNameOrOID())); 2141 } 2142 return false; 2143 } 2144 } 2145 } 2146 2147 // If we've gotten here, then things are OK. 2148 return true; 2149 } 2150 2151 private ObjectClass getParentStructuralObjectClass(final Entry entry, 2152 final SchemaValidationPolicy policy, final List<LocalizableMessage> ruleWarnings) { 2153 final Entry parentEntry; 2154 try { 2155 parentEntry = 2156 policy.checkDITStructureRulesEntryResolver().getEntry(entry.getName().parent()); 2157 } catch (final LdapException e) { 2158 if (ruleWarnings != null) { 2159 ruleWarnings.add(ERR_ENTRY_SCHEMA_DSR_PARENT_NOT_FOUND.get( 2160 entry.getName(), e.getResult().getDiagnosticMessage())); 2161 } 2162 return null; 2163 } 2164 2165 final ObjectClass parentStructuralObjectClass = 2166 Entries.getStructuralObjectClass(parentEntry, this); 2167 if (parentStructuralObjectClass == null) { 2168 if (ruleWarnings != null) { 2169 ruleWarnings.add(ERR_ENTRY_SCHEMA_DSR_NO_PARENT_OC.get(entry.getName())); 2170 } 2171 return null; 2172 } 2173 return parentStructuralObjectClass; 2174 } 2175}