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}