mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Gaetan Boismal
23.10.2015 39699e750bd08aee4aed1717b433b135fa007ac8
OPENDJ-1730 (CR-5831) Add fluent builder for DIT structure rules
1 files added
2 files modified
534 ■■■■ changed files
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java 237 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 116 ●●●●● patch | view | raw | blame | history
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DITStructureRuleTestCase.java 181 ●●●●● patch | view | raw | blame | history
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRule.java
@@ -22,17 +22,23 @@
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Portions copyright 2015 ForgeRock AS
 */
package org.forgerock.opendj.ldap.schema;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_DSR_UNKNOWN_NAME_FORM;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID;
import static com.forgerock.opendj.ldap.CoreMessages.ERR_DSR_VALIDATION_FAIL;
import static java.util.Arrays.*;
import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
import static com.forgerock.opendj.ldap.CoreMessages.*;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -45,6 +51,208 @@
 * of children that entries may have.
 */
public final class DITStructureRule extends SchemaElement {
    /** A fluent API for incrementally constructing DIT structure rules. */
    public static final class Builder extends SchemaElementBuilder<Builder> {
        private int ruleID;
        private final List<String> names = new LinkedList<String>();
        private boolean isObsolete;
        private String nameFormOID;
        private final Set<Integer> superiorRuleIDs = new LinkedHashSet<Integer>();
        Builder(final DITStructureRule structureRule, final SchemaBuilder builder) {
            super(builder);
            this.ruleID = structureRule.ruleID;
            this.names.addAll(structureRule.names);
            this.isObsolete = structureRule.isObsolete;
            this.nameFormOID = structureRule.nameFormOID;
            this.superiorRuleIDs.addAll(structureRule.superiorRuleIDs);
        }
        Builder(final Integer ruleID, final SchemaBuilder schemaBuilder) {
            super(schemaBuilder);
            this.ruleID = ruleID;
        }
        /**
         * Adds this DIT structure rule to the schema overwriting any existing
         * DIT structure rule with the same numeric ID.
         *
         * @return The parent schema builder.
         */
        public SchemaBuilder addToSchemaOverwrite() {
            return getSchemaBuilder().addDITStructureRule(new DITStructureRule(this), true);
        }
        /**
         * Adds this DIT structure rule to the schema, throwing an
         * {@code  ConflictingSchemaElementException} if there is an existing DIT
         * structure rule with the same numeric ID.
         *
         * @return The parent schema builder.
         * @throws ConflictingSchemaElementException
         *             If there is an existing structure rule with the same
         *             numeric ID.
         */
        public SchemaBuilder addToSchema() {
            return getSchemaBuilder().addDITStructureRule(new DITStructureRule(this), false);
        }
        @Override
        public Builder description(final String description) {
            return description0(description);
        }
        @Override
        public Builder extraProperties(final Map<String, List<String>> extraProperties) {
            return extraProperties0(extraProperties);
        }
        @Override
        public Builder extraProperties(final String extensionName, final String... extensionValues) {
            return extraProperties0(extensionName, extensionValues);
        }
        @Override
        Builder getThis() {
            return this;
        }
        /**
         * Sets the name form associated with the DIT structure rule.
         *
         * @param nameFormOID
         *            The name form numeric OID.
         * @return This builder.
         */
        public Builder nameForm(final String nameFormOID) {
            this.nameFormOID = nameFormOID;
            return this;
        }
        /**
         * Adds the provided user friendly names.
         *
         * @param names
         *            The user friendly names.
         * @return This builder.
         */
        public Builder names(final Collection<String> names) {
            this.names.addAll(names);
            return this;
        }
        /**
         * Adds the provided user friendly names.
         *
         * @param names
         *            The user friendly names.
         * @return This builder.
         */
        public Builder names(final String... names) {
            return names(asList(names));
        }
        /**
         * Specifies whether this schema element is obsolete.
         *
         * @param isObsolete
         *            {@code true} if this schema element is obsolete
         *            (default is {@code false}).
         * @return This builder.
         */
        public Builder obsolete(final boolean isObsolete) {
            this.isObsolete = isObsolete;
            return this;
        }
        @Override
        public Builder removeAllExtraProperties() {
            return removeAllExtraProperties0();
        }
        /**
         * Removes all user defined names.
         *
         * @return This builder.
         */
        public Builder removeAllNames() {
            this.names.clear();
            return this;
        }
        /**
         * Removes all superior rules.
         *
         * @return This builder.
         */
        public Builder removeAllSuperiorRules() {
            this.superiorRuleIDs.clear();
            return this;
        }
        @Override
        public Builder removeExtraProperty(final String extensionName, final String... extensionValues) {
            return removeExtraProperty0(extensionName, extensionValues);
        }
        /**
         * Removes the provided user defined name.
         *
         * @param name
         *            The user defined name to be removed.
         * @return This builder.
         */
        public Builder removeName(final String name) {
            this.names.remove(name);
            return this;
        }
        /**
         * Removes the provided superior rule.
         *
         * @param superiorRuleID
         *            The superior rule ID to be removed.
         * @return This builder.
         */
        public Builder removeSuperiorRule(final int superiorRuleID) {
            this.superiorRuleIDs.remove(superiorRuleID);
            return this;
        }
        /**
         * Sets the the numeric ID which uniquely identifies this structure rule.
         *
         * @param ruleID
         *            The numeric ID.
         * @return This builder.
         */
        public Builder ruleID(final int ruleID) {
            this.ruleID = ruleID;
            return this;
        }
        /**
         * Adds the provided superior rule identifiers.
         *
         * @param superiorRuleIDs
         *            Structure rule identifiers.
         * @return This builder.
         */
        public Builder superiorRules(final int... superiorRuleIDs) {
            for (int ruleID : superiorRuleIDs) {
                this.superiorRuleIDs.add(ruleID);
            }
            return this;
        }
        Builder superiorRules(final Collection<Integer> superiorRuleIDs) {
            this.superiorRuleIDs.addAll(superiorRuleIDs);
            return this;
        }
    }
    /** The rule ID for this DIT structure rule. */
    private final Integer ruleID;
@@ -69,17 +277,15 @@
    /** The indicates whether or not validation failed. */
    private boolean isValid;
    DITStructureRule(final Integer ruleID, final List<String> names, final String description,
            final boolean obsolete, final String nameFormOID, final Set<Integer> superiorRuleIDs,
            final Map<String, List<String>> extraProperties, final String definition) {
        super(description, extraProperties, definition);
    DITStructureRule(final Builder builder) {
        super(builder);
        Reject.ifNull(builder.nameFormOID);
        Reject.ifNull(ruleID, nameFormOID, superiorRuleIDs);
        this.ruleID = ruleID;
        this.names = names;
        this.isObsolete = obsolete;
        this.nameFormOID = nameFormOID;
        this.superiorRuleIDs = superiorRuleIDs;
        this.ruleID = builder.ruleID;
        this.names = unmodifiableCopyOfList(builder.names);
        this.isObsolete = builder.isObsolete;
        this.nameFormOID = builder.nameFormOID;
        this.superiorRuleIDs = unmodifiableCopyOfSet(builder.superiorRuleIDs);
    }
    /**
@@ -195,11 +401,6 @@
        return isObsolete;
    }
    DITStructureRule duplicate() {
        return new DITStructureRule(ruleID, names, getDescription(), isObsolete, nameFormOID,
                superiorRuleIDs, getExtraProperties(), toString());
    }
    @Override
    void toStringContent(final StringBuilder buffer) {
        buffer.append(ruleID);
opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -507,47 +507,6 @@
    /**
     * Adds the provided DIT structure rule definition to this schema builder.
     *
     * @param ruleID
     *            The rule identifier of the DIT structure rule.
     * @param names
     *            The user-friendly names of the DIT structure rule definition.
     * @param description
     *            The description of the DIT structure rule definition.
     * @param obsolete
     *            {@code true} if the DIT structure rule definition is obsolete,
     *            otherwise {@code false}.
     * @param nameForm
     *            The name form associated with the DIT structure rule.
     * @param superiorRules
     *            A list of superior rules (by rule id).
     * @param extraProperties
     *            A map containing additional properties associated with the DIT
     *            structure rule definition.
     * @param overwrite
     *            {@code true} if any existing DIT structure rule with the same
     *            OID should be overwritten.
     * @return A reference to this schema builder.
     * @throws ConflictingSchemaElementException
     *             If {@code overwrite} was {@code false} and a conflicting
     *             schema element was found.
     */
    SchemaBuilder addDITStructureRule(final Integer ruleID, final List<String> names,
            final String description, final boolean obsolete, final String nameForm,
            final Set<Integer> superiorRules, final Map<String, List<String>> extraProperties,
            final boolean overwrite) {
        lazyInitBuilder();
        final DITStructureRule rule =
                new DITStructureRule(ruleID, unmodifiableCopyOfList(names), description, obsolete,
                        nameForm, unmodifiableCopyOfSet(superiorRules),
                        unmodifiableCopyOfExtraProperties(extraProperties), null);
        addDITStructureRule(rule, overwrite);
        return this;
    }
    /**
     * Adds the provided DIT structure rule definition to this schema builder.
     *
     * @param definition
     *            The DIT structure rule definition.
     * @param overwrite
@@ -595,14 +554,8 @@
            reader.skipWhitespaces();
            // The next set of characters must be the OID.
            final Integer ruleID = readRuleID(reader);
            List<String> names = Collections.emptyList();
            String description = "".intern();
            boolean isObsolete = false;
            final DITStructureRule.Builder ruleBuilder = new DITStructureRule.Builder(readRuleID(reader), this);
            String nameForm = null;
            Set<Integer> superiorRules = Collections.emptySet();
            Map<String, List<String>> extraProperties = Collections.emptyMap();
            // At this point, we should have a pretty specific syntax that
            // describes what may come next, but some of the components are
@@ -619,31 +572,27 @@
                    // No more tokens.
                    break;
                } else if ("name".equalsIgnoreCase(tokenName)) {
                    names = readNameDescriptors(reader, allowsMalformedNamesAndOptions());
                    ruleBuilder.names(readNameDescriptors(reader, allowsMalformedNamesAndOptions()));
                } else if ("desc".equalsIgnoreCase(tokenName)) {
                    // This specifies the description for the attribute type. It
                    // is an arbitrary string of characters enclosed in single
                    // quotes.
                    description = readQuotedString(reader);
                    ruleBuilder.description(readQuotedString(reader));
                } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                    // This indicates whether the attribute type should be
                    // considered obsolete. We do not need to do any more
                    // parsing for this token.
                    isObsolete = true;
                    // considered obsolete.
                    ruleBuilder.obsolete(true);
                } else if ("form".equalsIgnoreCase(tokenName)) {
                    nameForm = readOID(reader, allowsMalformedNamesAndOptions());
                } else if ("sup".equalsIgnoreCase(tokenName)) {
                    superiorRules = readRuleIDs(reader);
                    ruleBuilder.superiorRules(readRuleIDs(reader));
                } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                    // This must be a non-standard property and it must be
                    // followed by either a single definition in single quotes
                    // or an open parenthesis followed by one or more values in
                    // single quotes separated by spaces followed by a close
                    // parenthesis.
                    if (extraProperties.isEmpty()) {
                        extraProperties = new HashMap<String, List<String>>();
                    }
                    extraProperties.put(tokenName, readExtensions(reader));
                    ruleBuilder.extraProperties(tokenName, readExtensions(reader));
                } else {
                    throw new LocalizedIllegalArgumentException(
                        ERR_ATTR_SYNTAX_DSR_ILLEGAL_TOKEN1.get(definition, tokenName));
@@ -653,20 +602,13 @@
            if (nameForm == null) {
                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(definition));
            }
            ruleBuilder.nameForm(nameForm);
            if (!extraProperties.isEmpty()) {
                extraProperties = Collections.unmodifiableMap(extraProperties);
            }
            final DITStructureRule rule =
                    new DITStructureRule(ruleID, names, description, isObsolete, nameForm,
                            superiorRules, extraProperties, definition);
            addDITStructureRule(rule, overwrite);
            return overwrite ? ruleBuilder.addToSchemaOverwrite() : ruleBuilder.addToSchema();
        } catch (final DecodeException e) {
            final LocalizableMessage msg = ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, e.getMessageObject());
            throw new LocalizedIllegalArgumentException(msg, e.getCause());
        }
        return this;
    }
    /**
@@ -970,6 +912,25 @@
    }
    /**
     * Returns a builder which can be used for incrementally constructing a new
     * DIT structure rule before adding it to the schema. Example usage:
     *
     * <pre>
     * SchemaBuilder builder = ...;
     * final int myRuleID = ...;
     * builder.buildDITStructureRule(myRuleID).name("DIT structure rule name").addToSchema();
     * </pre>
     *
     * @param ruleID
     *            The ID of the DIT structure rule.
     * @return A builder to continue building the DITStructureRule.
     */
    public DITStructureRule.Builder buildDITStructureRule(final int ruleID) {
        lazyInitBuilder();
        return new DITStructureRule.Builder(ruleID, this);
    }
    /**
     * Returns a builder which can be used for incrementally constructing a new matching rule before adding it to the
     * schema. Example usage:
     *
@@ -1257,6 +1218,21 @@
    }
    /**
     * Returns an DIT structure rule builder whose fields are initialized to the
     * values of the provided rule. This method should be used when duplicating
     * structure rules from external schemas or when modifying existing
     * structure rules.
     *
     * @param structureRule
     *            The DIT structure rule source.
     * @return A builder to continue building the DITStructureRule.
     */
    public DITStructureRule.Builder buildDITStructureRule(final DITStructureRule structureRule) {
        lazyInitBuilder();
        return new DITStructureRule.Builder(structureRule, this);
    }
    /**
     * Returns a matching rule builder whose fields are initialized to the
     * values of the provided matching rule. This method should be used when
     * duplicating matching rules from external schemas or when modifying
@@ -2251,7 +2227,7 @@
        return this;
    }
    private void addDITStructureRule(final DITStructureRule rule, final boolean overwrite) {
    SchemaBuilder addDITStructureRule(final DITStructureRule rule, final boolean overwrite) {
        DITStructureRule conflictingRule;
        if (id2StructureRules.containsKey(rule.getRuleID())) {
            conflictingRule = id2StructureRules.get(rule.getRuleID());
@@ -2278,6 +2254,8 @@
                rules.add(rule);
            }
        }
        return this;
    }
    private void addMatchingRuleUse(final MatchingRuleUse use, final boolean overwrite) {
@@ -2444,7 +2422,7 @@
        }
        for (final DITStructureRule structureRule : schema.getDITStuctureRules()) {
            addDITStructureRule(structureRule.duplicate(), overwrite);
            addDITStructureRule(structureRule, overwrite);
        }
    }
opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DITStructureRuleTestCase.java
New file
@@ -0,0 +1,181 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
 * or http://forgerock.org/license/CDDLv1.0.html.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at legal-notices/CDDLv1_0.txt.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Copyright 2015 ForgeRock AS.
 */
package org.forgerock.opendj.ldap.schema;
import static java.util.Collections.*;
import static org.fest.assertions.Assertions.*;
import static org.fest.assertions.MapAssert.*;
import org.testng.annotations.Test;
public class DITStructureRuleTestCase extends AbstractSchemaTestCase {
    private static final String NAME_FORM_TEST_OID = "1.2.3.4";
    /** Adds a valid DIT structure rule on the "" name form. */
    @Test
    public void testValidDITStructureRule() {
        final SchemaBuilder builder = getTestSchema()
               .buildDITStructureRule(42)
               .names("DIT structure rule test", "DIT structure rule for person")
               .nameForm(NAME_FORM_TEST_OID)
               .description("My DIT structure rule")
               .extraProperties("property name", "property value")
               .addToSchema();
        final Schema schema = builder.buildDITStructureRule(43)
               .names("DIT structure rule child test")
               .nameForm(NAME_FORM_TEST_OID)
               .superiorRules(42)
               .description("My DIT structure rule child")
               .extraProperties("property name", "property value")
               .addToSchema()
               .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final DITStructureRule sr = schema.getDITStructureRule(42);
        assertThat(sr).isNotNull();
        assertThat(sr.getRuleID()).isEqualTo(42);
        assertThat(sr.getNames()).containsOnly("DIT structure rule test", "DIT structure rule for person");
        assertThat(sr.getDescription()).isEqualTo("My DIT structure rule");
        assertThat(sr.getNameForm().getOID()).isEqualTo(NAME_FORM_TEST_OID);
        assertThat(sr.getExtraProperties()).includes(entry("property name", singletonList("property value")));
        assertThat(sr.getSuperiorRules()).isEmpty();
        assertThat(sr.isObsolete()).isFalse();
        final DITStructureRule srChild = schema.getDITStructureRule(43);
        assertThat(srChild).isNotNull();
        assertThat(srChild.getRuleID()).isEqualTo(43);
        assertThat(srChild.getNames()).containsOnly("DIT structure rule child test");
        assertThat(srChild.getDescription()).isEqualTo("My DIT structure rule child");
        assertThat(srChild.getNameForm().getOID()).isEqualTo(NAME_FORM_TEST_OID);
        assertThat(srChild.getExtraProperties()).includes(entry("property name", singletonList("property value")));
        assertThat(srChild.getSuperiorRules()).containsOnly(sr);
        assertThat(srChild.isObsolete()).isFalse();
    }
    @Test
    public void testCopyConstructor() throws Exception {
        final Schema schema = getTestSchema()
                .buildDITStructureRule(42)
                .names("DIT structure rule test")
                .nameForm(NAME_FORM_TEST_OID)
                .description("My DIT structure rule")
                .addToSchema()
                .toSchema();
        final Schema schemaCopy = getTestSchema()
                .buildDITStructureRule(schema.getDITStructureRule(42))
                .ruleID(43)
                .names("DIT structure rule test - copy")
                .addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        assertThat(schemaCopy.getWarnings()).isEmpty();
        final DITStructureRule srCopy = schemaCopy.getDITStructureRule(43);
        assertThat(srCopy).isNotNull();
        assertThat(srCopy.getRuleID()).isEqualTo(43);
        assertThat(srCopy.getNames()).containsOnly("DIT structure rule test", "DIT structure rule test - copy");
        assertThat(srCopy.getNameForm().getOID()).isEqualTo(NAME_FORM_TEST_OID);
        assertThat(srCopy.getDescription()).isEmpty();
        assertThat(srCopy.isObsolete()).isFalse();
    }
    @Test(expectedExceptions = ConflictingSchemaElementException.class)
    public void testBuilderDoesNotAllowOverwrite() throws Exception {
        final SchemaBuilder builder = getTestSchema()
                .buildDITStructureRule(42)
                .nameForm(NAME_FORM_TEST_OID)
                .addToSchema();
        builder.buildDITStructureRule(42)
               .nameForm(NAME_FORM_TEST_OID)
               .addToSchema()
               .toSchema();
    }
    @Test(expectedExceptions = NullPointerException.class)
    public void testBuilderDoesNotAllowNullNameForm() throws Exception {
        getTestSchema().buildDITStructureRule(42)
                       .nameForm(null)
                       .addToSchema();
    }
    @Test
    public void testBuilderRemoveAll() throws Exception {
        DITStructureRule.Builder srBuilder = getTestSchema()
                .buildDITStructureRule(42)
                .names("DIT structure rule test", "DIT structure rule for person")
                .nameForm(NAME_FORM_TEST_OID)
                .description("My DIT structure rule")
                .superiorRules(1, 2, 3, 4)
                .extraProperties("property name", "property value");
        Schema schema = srBuilder.removeAllNames()
                .removeAllSuperiorRules()
                .removeAllExtraProperties()
                .addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        DITStructureRule sr = schema.getDITStructureRule(42);
        assertThat(sr.getNames()).isEmpty();
        assertThat(sr.getExtraProperties()).isEmpty();
        assertThat(sr.getSuperiorRules()).isEmpty();
    }
    @Test
    public void testBuilderRemove() throws Exception {
        DITStructureRule.Builder srBuilder = getTestSchema()
                .buildDITStructureRule(42)
                .names("DIT structure rule test", "should be removed")
                .nameForm(NAME_FORM_TEST_OID)
                .description("My DIT structure rule")
                .superiorRules(1)
                .extraProperties("property name", "property value");
        Schema schema = srBuilder.removeName("should be removed")
                .removeSuperiorRule(1)
                .addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        DITStructureRule sr = schema.getDITStructureRule(42);
        assertThat(sr.getNames()).containsOnly("DIT structure rule test");
        assertThat(sr.getSuperiorRules()).isEmpty();
    }
    private SchemaBuilder getTestSchema() {
        return new SchemaBuilder(Schema.getCoreSchema())
                .buildNameForm(NAME_FORM_TEST_OID)
                .structuralObjectClassOID("person")
                .requiredAttributes("sn", "cn")
                .addToSchema();
    }
}