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