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