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 Sun Microsystems, Inc.
025 *      Portions copyright 2011-2015 ForgeRock AS
026 */
027package org.forgerock.opendj.ldap.schema;
028
029import static com.forgerock.opendj.ldap.CoreMessages.*;
030import static java.util.Arrays.*;
031
032import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
033
034import java.util.Collection;
035import java.util.Collections;
036import java.util.HashSet;
037import java.util.Iterator;
038import java.util.LinkedHashSet;
039import java.util.LinkedList;
040import java.util.List;
041import java.util.Map;
042import java.util.Set;
043
044import org.forgerock.i18n.LocalizableMessage;
045import org.forgerock.i18n.LocalizableMessageDescriptor.Arg2;
046import org.forgerock.util.Reject;
047
048/**
049 * This class defines a DIT content rule, which defines the set of allowed,
050 * required, and prohibited attributes for entries with a given structural
051 * objectclass, and also indicates which auxiliary classes may be included in
052 * the entry.
053 */
054public final class DITContentRule extends SchemaElement {
055
056    /** A fluent API for incrementally constructing DIT content rule. */
057    public static final class Builder extends SchemaElementBuilder<Builder> {
058        private String structuralClassOID;
059        private final List<String> names = new LinkedList<>();
060        private boolean isObsolete;
061        private final Set<String> auxiliaryClassOIDs = new LinkedHashSet<>();
062        private final Set<String> optionalAttributeOIDs = new LinkedHashSet<>();
063        private final Set<String> prohibitedAttributeOIDs = new LinkedHashSet<>();
064        private final Set<String> requiredAttributeOIDs = new LinkedHashSet<>();
065
066        Builder(final DITContentRule contentRule, final SchemaBuilder schemaBuilder) {
067            super(schemaBuilder, contentRule);
068            structuralClassOID = contentRule.structuralClassOID;
069            names.addAll(contentRule.getNames());
070            isObsolete = contentRule.isObsolete;
071            auxiliaryClassOIDs.addAll(contentRule.auxiliaryClassOIDs);
072            optionalAttributeOIDs.addAll(contentRule.optionalAttributeOIDs);
073            prohibitedAttributeOIDs.addAll(contentRule.prohibitedAttributeOIDs);
074            requiredAttributeOIDs.addAll(contentRule.requiredAttributeOIDs);
075        }
076
077        Builder(final String structuralClassOID, final SchemaBuilder builder) {
078            super(builder);
079            this.structuralClassOID = structuralClassOID;
080        }
081
082        /**
083         * Adds this DIT content rule to the schema, throwing a
084         * {@code  ConflictingSchemaElementException} if there is an existing DIT
085         * content rule with the same structural object class OID.
086         *
087         * @return The parent schema builder.
088         * @throws ConflictingSchemaElementException
089         *             If there is an existing DIT content rule with the same
090         *             structural object class OID.
091         */
092        public SchemaBuilder addToSchema() {
093            return getSchemaBuilder().addDITContentRule(new DITContentRule(this), false);
094        }
095
096        /**
097         * Adds this DIT content rule to the schema overwriting any existing
098         * content rule with the same structural class OID.
099         *
100         * @return The parent schema builder.
101         */
102        public SchemaBuilder addToSchemaOverwrite() {
103            return getSchemaBuilder().addDITContentRule(new DITContentRule(this), true);
104        }
105
106        /**
107         * Adds the provided auxiliary classes to the list of auxiliary object
108         * classes that entries subject to this DIT content rule may belong to.
109         *
110         * @param objectClassNamesOrOIDs
111         *            The list of auxiliary class names or OIDs.
112         * @return This builder.
113         */
114        public Builder auxiliaryObjectClasses(final Collection<String> objectClassNamesOrOIDs) {
115            this.auxiliaryClassOIDs.addAll(objectClassNamesOrOIDs);
116            return this;
117        }
118
119        /**
120         * Adds the provided auxiliary classes to the list of auxiliary object
121         * classes that entries subject to this DIT content rule may belong to.
122         *
123         * @param objectClassNamesOrOIDs
124         *            The list of auxiliary class names or OIDs.
125         * @return This builder.
126         */
127        public Builder auxiliaryObjectClasses(String... objectClassNamesOrOIDs) {
128            this.auxiliaryClassOIDs.addAll(asList(objectClassNamesOrOIDs));
129            return this;
130        }
131
132        @Override
133        public Builder description(final String description) {
134            return description0(description);
135        }
136
137        @Override
138        public Builder extraProperties(final Map<String, List<String>> extraProperties) {
139            return extraProperties0(extraProperties);
140        }
141
142        @Override
143        public Builder extraProperties(final String extensionName, final String... extensionValues) {
144            return extraProperties0(extensionName, extensionValues);
145        }
146
147        @Override
148        Builder getThis() {
149            return this;
150        }
151
152        /**
153         * Adds the provided user friendly names.
154         *
155         * @param names
156         *            The user friendly names.
157         * @return This builder.
158         */
159        public Builder names(final Collection<String> names) {
160            this.names.addAll(names);
161            return this;
162        }
163
164        /**
165         * Adds the provided user friendly names.
166         *
167         * @param names
168         *            The user friendly names.
169         * @return This builder.
170         */
171        public Builder names(final String... names) {
172            return names(asList(names));
173        }
174
175        /**
176         * Specifies whether this schema element is obsolete.
177         *
178         * @param isObsolete
179         *            {@code true} if this schema element is obsolete (default
180         *            is {@code false}).
181         * @return This builder.
182         */
183        public Builder obsolete(final boolean isObsolete) {
184            this.isObsolete = isObsolete;
185            return this;
186        }
187
188        /**
189         * Adds the provided optional attributes to the list of attribute types
190         * that entries subject to this DIT content rule may contain.
191         *
192         * @param attributeNamesOrOIDs
193         *            The list of optional attribute names or OIDs.
194         * @return This builder.
195         */
196        public Builder optionalAttributes(final Collection<String> attributeNamesOrOIDs) {
197            this.optionalAttributeOIDs.addAll(attributeNamesOrOIDs);
198            return this;
199        }
200
201        /**
202         * Adds the provided optional attributes to the list of attribute types
203         * that entries subject to this DIT content rule may contain.
204         *
205         * @param attributeNamesOrOIDs
206         *            The list of optional attribute names or OIDs.
207         * @return This builder.
208         */
209        public Builder optionalAttributes(final String... attributeNamesOrOIDs) {
210            this.optionalAttributeOIDs.addAll(asList(attributeNamesOrOIDs));
211            return this;
212        }
213
214        /**
215         * Adds the provided prohibited attributes to the list of attribute types
216         * that entries subject to this DIT content rule must not contain.
217         *
218         * @param attributeNamesOrOIDs
219         *            The list of prohibited attribute names or OIDs.
220         * @return This builder.
221         */
222        public Builder prohibitedAttributes(final Collection<String> attributeNamesOrOIDs) {
223            this.prohibitedAttributeOIDs.addAll(attributeNamesOrOIDs);
224            return this;
225        }
226
227        /**
228         * Adds the provided prohibited attributes to the list of attribute types
229         * that entries subject to this DIT content rule must not contain.
230         *
231         * @param attributeNamesOrOIDs
232         *            The list of prohibited attribute names or OIDs.
233         * @return This builder.
234         */
235        public Builder prohibitedAttributes(final String... attributeNamesOrOIDs) {
236            this.prohibitedAttributeOIDs.addAll(asList(attributeNamesOrOIDs));
237            return this;
238        }
239
240        /**
241         * Clears the list of auxiliary object classes that entries subject to
242         * this DIT content rule may belong to.
243         *
244         * @return This builder.
245         */
246        public Builder removeAllAuxiliaryObjectClasses() {
247            this.auxiliaryClassOIDs.clear();
248            return this;
249        }
250
251        @Override
252        public Builder removeAllExtraProperties() {
253            return removeAllExtraProperties0();
254        }
255
256        /**
257         * Removes all user defined names.
258         *
259         * @return This builder.
260         */
261        public Builder removeAllNames() {
262            this.names.clear();
263            return this;
264        }
265
266        /**
267         * Clears the list of attribute types that entries subject to this DIT
268         * content rule may contain.
269         *
270         * @return This builder.
271         */
272        public Builder removeAllOptionalAttributes() {
273            this.optionalAttributeOIDs.clear();
274            return this;
275        }
276
277        /**
278         * Clears the list of attribute types that entries subject to this DIT
279         * content rule must not contain.
280         *
281         * @return This builder.
282         */
283        public Builder removeAllProhibitedAttributes() {
284            this.prohibitedAttributeOIDs.clear();
285            return this;
286        }
287
288        /**
289         * Clears the list of attribute types that entries subject to this DIT
290         * content rule must contain.
291         *
292         * @return This builder.
293         */
294        public Builder removeAllRequiredAttributes() {
295            this.requiredAttributeOIDs.clear();
296            return this;
297        }
298
299        /**
300         * Removes the provided object class in the list of auxiliary object classes that entries subject to
301         * this DIT content rule may belong to.
302         *
303         * @param objectClassNameOrOID
304         *            The auxiliary object class name or OID to be removed.
305         * @return This builder.
306         */
307        public Builder removeAuxiliaryObjectClass(String objectClassNameOrOID) {
308            this.auxiliaryClassOIDs.remove(objectClassNameOrOID);
309            return this;
310        }
311
312        @Override
313        public Builder removeExtraProperty(String extensionName, String... extensionValues) {
314            return removeExtraProperty0(extensionName, extensionValues);
315        }
316
317        /**
318         * Removes the provided user defined name.
319         *
320         * @param name
321         *            The user defined name to be removed.
322         * @return This builder.
323         */
324        public Builder removeName(String name) {
325            this.names.remove(name);
326            return this;
327        }
328
329        /**
330         * Removes the provided optional attribute in the list of attribute
331         * types that entries subject to this DIT content rule may contain.
332         *
333         * @param attributeNameOrOID
334         *            The optional attribute name or OID to be removed.
335         * @return This builder.
336         */
337        public Builder removeOptionalAttribute(String attributeNameOrOID) {
338            this.optionalAttributeOIDs.remove(attributeNameOrOID);
339            return this;
340        }
341
342        /**
343         * Removes the provided prohibited attribute in the list of attribute
344         * types that entries subject to this DIT content rule must not contain.
345         *
346         * @param attributeNameOrOID
347         *            The prohibited attribute name or OID to be removed.
348         * @return This builder.
349         */
350        public Builder removeProhibitedAttribute(String attributeNameOrOID) {
351            this.prohibitedAttributeOIDs.remove(attributeNameOrOID);
352            return this;
353        }
354
355        /**
356         * Removes the provided required attribute in the list of attribute
357         * types that entries subject to this DIT content rule must contain.
358         *
359         * @param attributeNameOrOID
360         *            The provided required attribute name or OID to be removed.
361         * @return This builder.
362         */
363        public Builder removeRequiredAttribute(String attributeNameOrOID) {
364            this.requiredAttributeOIDs.remove(attributeNameOrOID);
365            return this;
366        }
367
368        /**
369         * Adds the provided attribute to the list of attribute types that
370         * entries subject to this DIT content rule must contain.
371         *
372         * @param attributeNamesOrOIDs
373         *            The list of required attribute names or OIDs.
374         * @return This builder.
375         */
376        public Builder requiredAttributes(final Collection<String> attributeNamesOrOIDs) {
377            this.requiredAttributeOIDs.addAll(attributeNamesOrOIDs);
378            return this;
379        }
380
381        /**
382         * Adds the provided attribute to the list of attribute types that
383         * entries subject to this DIT content rule must contain.
384         *
385         * @param attributeNamesOrOIDs
386         *            The list of required attribute names or OIDs.
387         * @return This builder.
388         */
389        public Builder requiredAttributes(final String... attributeNamesOrOIDs) {
390            this.requiredAttributeOIDs.addAll(asList(attributeNamesOrOIDs));
391            return this;
392        }
393
394        /**
395         * Sets the structural class OID which uniquely identifies this DIT
396         * content rule.
397         *
398         * @param strucuralClassOID
399         *            The numeric OID.
400         * @return This builder.
401         */
402        public Builder structuralClassOID(String strucuralClassOID) {
403            this.structuralClassOID = strucuralClassOID;
404            return this;
405        }
406
407    }
408
409    /** The structural objectclass for this DIT content rule. */
410    private final String structuralClassOID;
411
412    /** The set of user defined names for this definition. */
413    private final List<String> names;
414
415    /** Indicates whether this definition is declared "obsolete". */
416    private final boolean isObsolete;
417
418    /**
419     * The set of auxiliary objectclasses that entries with this content
420     * rule may contain, in a mapping between the objectclass and the
421     * user-defined name for that class.
422     */
423    private final Set<String> auxiliaryClassOIDs;
424
425    /** The set of optional attribute types for this DIT content rule. */
426    private final Set<String> optionalAttributeOIDs;
427
428    /** The set of prohibited attribute types for this DIT content rule. */
429    private final Set<String> prohibitedAttributeOIDs;
430
431    /** The set of required attribute types for this DIT content rule. */
432    private final Set<String> requiredAttributeOIDs;
433
434    private ObjectClass structuralClass;
435    private Set<ObjectClass> auxiliaryClasses = Collections.emptySet();
436    private Set<AttributeType> optionalAttributes = Collections.emptySet();
437    private Set<AttributeType> prohibitedAttributes = Collections.emptySet();
438    private Set<AttributeType> requiredAttributes = Collections.emptySet();
439
440    private DITContentRule(final Builder builder) {
441        super(builder);
442        Reject.ifNull(builder.structuralClassOID);
443
444        structuralClassOID = builder.structuralClassOID;
445        names = unmodifiableCopyOfList(builder.names);
446        isObsolete = builder.isObsolete;
447        auxiliaryClassOIDs = unmodifiableCopyOfSet(builder.auxiliaryClassOIDs);
448        optionalAttributeOIDs = unmodifiableCopyOfSet(builder.optionalAttributeOIDs);
449        prohibitedAttributeOIDs = unmodifiableCopyOfSet(builder.prohibitedAttributeOIDs);
450        requiredAttributeOIDs = unmodifiableCopyOfSet(builder.requiredAttributeOIDs);
451    }
452
453    /**
454     * Returns {@code true} if the provided object is a DIT content rule having
455     * the same structural object class OID as this DIT content rule.
456     *
457     * @param o
458     *            The object to be compared.
459     * @return {@code true} if the provided object is a DIT content rule having
460     *         the same numeric OID as this DIT content rule.
461     */
462    @Override
463    public boolean equals(final Object o) {
464        if (this == o) {
465            return true;
466        } else if (o instanceof DITContentRule) {
467            final DITContentRule other = (DITContentRule) o;
468            return structuralClassOID.equals(other.structuralClassOID);
469        } else {
470            return false;
471        }
472    }
473
474    /**
475     * Returns an unmodifiable set containing the auxiliary objectclasses that
476     * may be used for entries associated with this DIT content rule.
477     *
478     * @return An unmodifiable set containing the auxiliary objectclasses that
479     *         may be used for entries associated with this DIT content rule.
480     */
481    public Set<ObjectClass> getAuxiliaryClasses() {
482        return auxiliaryClasses;
483    }
484
485    /**
486     * Returns the name or structural class OID for this schema definition. If
487     * it has one or more names, then the primary name will be returned. If it
488     * does not have any names, then the OID will be returned.
489     *
490     * @return The name or OID for this schema definition.
491     */
492    public String getNameOrOID() {
493        if (names.isEmpty()) {
494            return structuralClassOID;
495        }
496        return names.get(0);
497    }
498
499    /**
500     * Returns an unmodifiable list containing the user-defined names that may
501     * be used to reference this schema definition.
502     *
503     * @return Returns an unmodifiable list containing the user-defined names
504     *         that may be used to reference this schema definition.
505     */
506    public List<String> getNames() {
507        return names;
508    }
509
510    /**
511     * Returns an unmodifiable set containing the optional attributes for this
512     * DIT content rule.
513     *
514     * @return An unmodifiable set containing the optional attributes for this
515     *         DIT content rule.
516     */
517    public Set<AttributeType> getOptionalAttributes() {
518        return optionalAttributes;
519    }
520
521    /**
522     * Returns an unmodifiable set containing the prohibited attributes for this
523     * DIT content rule.
524     *
525     * @return An unmodifiable set containing the prohibited attributes for this
526     *         DIT content rule.
527     */
528    public Set<AttributeType> getProhibitedAttributes() {
529        return prohibitedAttributes;
530    }
531
532    /**
533     * Returns an unmodifiable set containing the required attributes for this
534     * DIT content rule.
535     *
536     * @return An unmodifiable set containing the required attributes for this
537     *         DIT content rule.
538     */
539    public Set<AttributeType> getRequiredAttributes() {
540        return requiredAttributes;
541    }
542
543    /**
544     * Returns the structural objectclass for this DIT content rule.
545     *
546     * @return The structural objectclass for this DIT content rule.
547     */
548    public ObjectClass getStructuralClass() {
549        return structuralClass;
550    }
551
552    /**
553     * Returns the structural class OID for this schema definition.
554     *
555     * @return The structural class OID for this schema definition.
556     */
557    public String getStructuralClassOID() {
558        return structuralClassOID;
559    }
560
561    /**
562     * Returns the hash code for this DIT content rule. It will be calculated as
563     * the hash code of the structural object class OID.
564     *
565     * @return The hash code for this DIT content rule.
566     */
567    @Override
568    public int hashCode() {
569        return structuralClassOID.hashCode();
570    }
571
572    /**
573     * Indicates whether this schema definition has the specified name.
574     *
575     * @param name
576     *            The name for which to make the determination.
577     * @return <code>true</code> if the specified name is assigned to this
578     *         schema definition, or <code>false</code> if not.
579     */
580    public boolean hasName(final String name) {
581        for (final String n : names) {
582            if (n.equalsIgnoreCase(name)) {
583                return true;
584            }
585        }
586        return false;
587    }
588
589    /**
590     * Indicates whether this schema definition has the specified name or
591     * structural class OID.
592     *
593     * @param value
594     *            The value for which to make the determination.
595     * @return <code>true</code> if the provided value matches the OID or one of
596     *         the names assigned to this schema definition, or
597     *         <code>false</code> if not.
598     */
599    public boolean hasNameOrOID(final String value) {
600        return hasName(value) || structuralClassOID.equals(value);
601    }
602
603    /**
604     * Indicates whether this schema definition is declared "obsolete".
605     *
606     * @return <code>true</code> if this schema definition is declared
607     *         "obsolete", or <code>false</code> if not.
608     */
609    public boolean isObsolete() {
610        return isObsolete;
611    }
612
613    /**
614     * Indicates whether the provided attribute type is included in the optional
615     * attribute list for this DIT content rule.
616     *
617     * @param attributeType
618     *            The attribute type for which to make the determination.
619     * @return <code>true</code> if the provided attribute type is optional for
620     *         this DIT content rule, or <code>false</code> if not.
621     */
622    public boolean isOptional(final AttributeType attributeType) {
623        return optionalAttributes.contains(attributeType);
624    }
625
626    /**
627     * Indicates whether the provided attribute type is included in the required
628     * attribute list for this DIT content rule.
629     *
630     * @param attributeType
631     *            The attribute type for which to make the determination.
632     * @return <code>true</code> if the provided attribute type is required by
633     *         this DIT content rule, or <code>false</code> if not.
634     */
635    public boolean isRequired(final AttributeType attributeType) {
636        return requiredAttributes.contains(attributeType);
637    }
638
639    /**
640     * Indicates whether the provided attribute type is in the list of required
641     * or optional attributes for this DIT content rule.
642     *
643     * @param attributeType
644     *            The attribute type for which to make the determination.
645     * @return <code>true</code> if the provided attribute type is required or
646     *         allowed for this DIT content rule, or <code>false</code> if it is
647     *         not.
648     */
649    public boolean isRequiredOrOptional(final AttributeType attributeType) {
650        return isRequired(attributeType) || isOptional(attributeType);
651    }
652
653    @Override
654    void toStringContent(final StringBuilder buffer) {
655        buffer.append(structuralClassOID);
656
657        if (!names.isEmpty()) {
658            final Iterator<String> iterator = names.iterator();
659
660            final String firstName = iterator.next();
661            if (iterator.hasNext()) {
662                buffer.append(" NAME ( '");
663                buffer.append(firstName);
664
665                while (iterator.hasNext()) {
666                    buffer.append("' '");
667                    buffer.append(iterator.next());
668                }
669
670                buffer.append("' )");
671            } else {
672                buffer.append(" NAME '");
673                buffer.append(firstName);
674                buffer.append("'");
675            }
676        }
677
678        appendDescription(buffer);
679
680        if (isObsolete) {
681            buffer.append(" OBSOLETE");
682        }
683
684        if (!auxiliaryClassOIDs.isEmpty()) {
685            final Iterator<String> iterator = auxiliaryClassOIDs.iterator();
686
687            final String firstClass = iterator.next();
688            if (iterator.hasNext()) {
689                buffer.append(" AUX (");
690                buffer.append(firstClass);
691
692                while (iterator.hasNext()) {
693                    buffer.append(" $ ");
694                    buffer.append(iterator.next());
695                }
696
697                buffer.append(" )");
698            } else {
699                buffer.append(" AUX ");
700                buffer.append(firstClass);
701            }
702        }
703
704        if (!requiredAttributeOIDs.isEmpty()) {
705            final Iterator<String> iterator = requiredAttributeOIDs.iterator();
706
707            final String firstName = iterator.next();
708            if (iterator.hasNext()) {
709                buffer.append(" MUST ( ");
710                buffer.append(firstName);
711
712                while (iterator.hasNext()) {
713                    buffer.append(" $ ");
714                    buffer.append(iterator.next());
715                }
716
717                buffer.append(" )");
718            } else {
719                buffer.append(" MUST ");
720                buffer.append(firstName);
721            }
722        }
723
724        if (!optionalAttributeOIDs.isEmpty()) {
725            final Iterator<String> iterator = optionalAttributeOIDs.iterator();
726
727            final String firstName = iterator.next();
728            if (iterator.hasNext()) {
729                buffer.append(" MAY ( ");
730                buffer.append(firstName);
731
732                while (iterator.hasNext()) {
733                    buffer.append(" $ ");
734                    buffer.append(iterator.next());
735                }
736
737                buffer.append(" )");
738            } else {
739                buffer.append(" MAY ");
740                buffer.append(firstName);
741            }
742        }
743
744        if (!prohibitedAttributeOIDs.isEmpty()) {
745            final Iterator<String> iterator = prohibitedAttributeOIDs.iterator();
746
747            final String firstName = iterator.next();
748            if (iterator.hasNext()) {
749                buffer.append(" NOT ( ");
750                buffer.append(firstName);
751
752                while (iterator.hasNext()) {
753                    buffer.append(" $ ");
754                    buffer.append(iterator.next());
755                }
756
757                buffer.append(" )");
758            } else {
759                buffer.append(" NOT ");
760                buffer.append(firstName);
761            }
762        }
763    }
764
765    void validate(final Schema schema, final List<LocalizableMessage> warnings)
766            throws SchemaException {
767        // Get the objectclass with the specified OID. If it does not exist
768        // or is not structural, then fail.
769        if (structuralClassOID != null) {
770            try {
771                structuralClass = schema.getObjectClass(structuralClassOID);
772            } catch (final UnknownSchemaElementException e) {
773                final LocalizableMessage message =
774                        ERR_ATTR_SYNTAX_DCR_UNKNOWN_STRUCTURAL_CLASS1.get(getNameOrOID(),
775                                structuralClassOID);
776                throw new SchemaException(message, e);
777            }
778            if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL) {
779                final LocalizableMessage message =
780                        ERR_ATTR_SYNTAX_DCR_STRUCTURAL_CLASS_NOT_STRUCTURAL1.get(getNameOrOID(),
781                                structuralClass.getNameOrOID(), structuralClass
782                                        .getObjectClassType().toString());
783                warnings.add(message);
784            }
785        }
786
787        if (!auxiliaryClassOIDs.isEmpty()) {
788            auxiliaryClasses = new HashSet<>(auxiliaryClassOIDs.size());
789            ObjectClass objectClass;
790            for (final String oid : auxiliaryClassOIDs) {
791                try {
792                    objectClass = schema.getObjectClass(oid);
793                } catch (final UnknownSchemaElementException e) {
794                    // This isn't good because it is an unknown auxiliary class.
795                    final LocalizableMessage message =
796                            ERR_ATTR_SYNTAX_DCR_UNKNOWN_AUXILIARY_CLASS1.get(getNameOrOID(), oid);
797                    throw new SchemaException(message, e);
798                }
799                if (objectClass.getObjectClassType() != ObjectClassType.AUXILIARY) {
800                    // This isn't good because it isn't an auxiliary class.
801                    final LocalizableMessage message =
802                            ERR_ATTR_SYNTAX_DCR_AUXILIARY_CLASS_NOT_AUXILIARY1.get(getNameOrOID(),
803                                    structuralClass.getOID(), structuralClass.getObjectClassType()
804                                            .toString());
805                    throw new SchemaException(message);
806                }
807                auxiliaryClasses.add(objectClass);
808            }
809        }
810
811        if (!requiredAttributeOIDs.isEmpty()) {
812            requiredAttributes =
813                getAttributeTypes(schema, requiredAttributeOIDs, ERR_ATTR_SYNTAX_DCR_UNKNOWN_REQUIRED_ATTR1);
814        }
815
816        if (!optionalAttributeOIDs.isEmpty()) {
817            optionalAttributes =
818                getAttributeTypes(schema, optionalAttributeOIDs, ERR_ATTR_SYNTAX_DCR_UNKNOWN_OPTIONAL_ATTR1);
819        }
820
821        if (!prohibitedAttributeOIDs.isEmpty()) {
822            prohibitedAttributes =
823                getAttributeTypes(schema, prohibitedAttributeOIDs, ERR_ATTR_SYNTAX_DCR_UNKNOWN_PROHIBITED_ATTR1);
824        }
825
826        // Make sure that none of the prohibited attributes is required by
827        // the structural or any of the auxiliary classes.
828        for (final AttributeType t : prohibitedAttributes) {
829            if (structuralClass.isRequired(t)) {
830                final LocalizableMessage message =
831                        ERR_ATTR_SYNTAX_DCR_PROHIBITED_REQUIRED_BY_STRUCTURAL.get(getNameOrOID(), t
832                                .getNameOrOID(), structuralClass.getNameOrOID());
833                throw new SchemaException(message);
834            }
835
836            for (final ObjectClass oc : auxiliaryClasses) {
837                if (oc.isRequired(t)) {
838                    final LocalizableMessage message =
839                            ERR_ATTR_SYNTAX_DCR_PROHIBITED_REQUIRED_BY_AUXILIARY.get(
840                                    getNameOrOID(), t.getNameOrOID(), oc.getNameOrOID());
841                    throw new SchemaException(message);
842                }
843            }
844        }
845
846        auxiliaryClasses = Collections.unmodifiableSet(auxiliaryClasses);
847        optionalAttributes = Collections.unmodifiableSet(optionalAttributes);
848        prohibitedAttributes = Collections.unmodifiableSet(prohibitedAttributes);
849        requiredAttributes = Collections.unmodifiableSet(requiredAttributes);
850    }
851
852    private Set<AttributeType> getAttributeTypes(final Schema schema, Set<String> oids, Arg2<Object, Object> errorMsg)
853            throws SchemaException {
854        Set<AttributeType> attrTypes = new HashSet<>(oids.size());
855        for (final String oid : oids) {
856            try {
857                attrTypes.add(schema.getAttributeType(oid));
858            } catch (final UnknownSchemaElementException e) {
859                // This isn't good because it means that the DIT content rule
860                // requires an attribute type that we don't know anything about.
861                throw new SchemaException(errorMsg.get(getNameOrOID(), oid), e);
862            }
863        }
864        return attrTypes;
865    }
866}