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

Matthew Swift
07.27.2016 ec746ba5b05f116fbf8a4b674e9aebc7a5f72b07
OPENDJSDK-97 Support enumerations in Syntax builder

* delegate construction of enumeration syntax and matching rule to
Syntax.Builder#addToSchema(boolean) and route all construction paths
through this method in order to have consistent behavior

* add user friendly name for enumeration matching rules and ensure that
removal uses the OID rather than the name

* fix unit tests which were dependent on previous broken behavior.
5 files modified
534 ■■■■ changed files
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnumSyntaxImpl.java 2 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java 66 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java 28 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java 167 ●●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java 271 ●●●● patch | view | raw | blame | history
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnumSyntaxImpl.java
@@ -33,7 +33,6 @@
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.opendj.ldap.ByteSequence;
import org.forgerock.opendj.ldap.ByteString;
import org.forgerock.util.Reject;
/**
 * This class provides an enumeration-based mechanism where a new syntax and its
@@ -46,7 +45,6 @@
    private final List<String> entries;
    EnumSyntaxImpl(final String oid, final List<String> entries) {
        Reject.ifNull(oid, entries);
        this.oid = oid;
        final List<String> entryStrings = new ArrayList<>(entries.size());
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -31,7 +31,6 @@
import static com.forgerock.opendj.util.StaticUtils.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -615,33 +614,10 @@
    public SchemaBuilder addEnumerationSyntax(final String oid, final String description,
            final boolean overwrite, final String... enumerations) {
        Reject.ifNull((Object) enumerations);
        lazyInitBuilder();
        final List<String> enumEntries = Arrays.asList(enumerations);
        final Syntax.Builder syntaxBuilder = buildSyntax(oid).description(description)
            .extraProperties(Collections.singletonMap("X-ENUM", enumEntries));
        addEnumerationSyntax0(syntaxBuilder, oid, enumEntries, overwrite);
        return this;
    }
    private void addEnumerationSyntax0(final Syntax.Builder syntaxBuilder,
            final String oid, final List<String> enumEntries, final boolean overwrite) {
        final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, enumEntries);
        syntaxBuilder
                .implementation(enumImpl)
        return buildSyntax(oid)
                .description(description)
                .extraProperties("X-ENUM", enumerations)
                .addToSchema(overwrite);
        try {
            buildMatchingRule(enumImpl.getOrderingMatchingRule())
                    .names(OMR_GENERIC_ENUM_NAME + "." + oid)
                    .syntaxOID(oid)
                    .extraProperties(CoreSchemaImpl.OPENDS_ORIGIN)
                    .implementation(new EnumOrderingMatchingRule(enumImpl))
                    .addToSchemaOverwrite();
        } catch (final ConflictingSchemaElementException e) {
            removeSyntax(oid);
        }
    }
    /**
@@ -1400,15 +1376,10 @@
    public SchemaBuilder addPatternSyntax(final String oid, final String description,
            final Pattern pattern, final boolean overwrite) {
        Reject.ifNull(pattern);
        lazyInitBuilder();
        final Syntax.Builder syntaxBuilder = buildSyntax(oid).description(description).extraProperties(
                Collections.singletonMap("X-PATTERN", Collections.singletonList(pattern.toString())));
        syntaxBuilder.addToSchema(overwrite);
        return this;
        return buildSyntax(oid)
                .description(description)
                .extraProperties("X-PATTERN", pattern.toString())
                .addToSchema(overwrite);
    }
    /**
@@ -1726,15 +1697,10 @@
    public SchemaBuilder addSubstitutionSyntax(final String oid, final String description,
            final String substituteSyntax, final boolean overwrite) {
        Reject.ifNull(substituteSyntax);
        lazyInitBuilder();
        final Syntax.Builder syntaxBuilder = buildSyntax(oid).description(description).extraProperties(
                Collections.singletonMap("X-SUBST", Collections.singletonList(substituteSyntax)));
        syntaxBuilder.addToSchema(overwrite);
        return this;
        return buildSyntax(oid)
                .description(description)
                .extraProperties("X-SUBST", substituteSyntax)
                .addToSchema(overwrite);
    }
    /**
@@ -1821,14 +1787,6 @@
                }
            }
            // See if it is a enum syntax
            for (final Map.Entry<String, List<String>> property : syntaxBuilder.getExtraProperties().entrySet()) {
                if ("x-enum".equalsIgnoreCase(property.getKey())) {
                    addEnumerationSyntax0(syntaxBuilder, oid, property.getValue(), overwrite);
                    return this;
                }
            }
            syntaxBuilder.addToSchema(overwrite);
        } catch (final DecodeException e) {
            final LocalizableMessage msg =
@@ -2557,7 +2515,7 @@
    private void removeSyntax(final Syntax syntax) {
        for (Map.Entry<String, List<String>> property : syntax.getExtraProperties().entrySet()) {
            if ("x-enum".equalsIgnoreCase(property.getKey())) {
                removeMatchingRule(OMR_GENERIC_ENUM_NAME + "." + syntax.getOID());
                removeMatchingRule(OMR_OID_GENERIC_ENUM + "." + syntax.getOID());
                break;
            }
        }
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Syntax.java
@@ -72,7 +72,7 @@
         *             If there is an existing syntax with the same numeric OID.
         */
        public SchemaBuilder addToSchema() {
            return getSchemaBuilder().addSyntax(new Syntax(this), false);
            return addToSchema(false);
        }
        /**
@@ -81,19 +81,25 @@
         * @return The parent schema builder.
         */
        public SchemaBuilder addToSchemaOverwrite() {
            return getSchemaBuilder().addSyntax(new Syntax(this), true);
            return addToSchema(true);
        }
        /**
         * Adds this syntax to the schema - overwriting any existing syntax with the same numeric OID
         * if the overwrite parameter is set to {@code true}.
         *
         * @param overwrite
         *            {@code true} if any syntax with the same OID should be overwritten.
         * @return The parent schema builder.
         */
        SchemaBuilder addToSchema(final boolean overwrite) {
            return overwrite ? addToSchemaOverwrite() : addToSchema();
            // Enumeration syntaxes will need their associated matching rule registered now as well.
            for (final Map.Entry<String, List<String>> property : getExtraProperties().entrySet()) {
                if ("x-enum".equalsIgnoreCase(property.getKey())) {
                    final EnumSyntaxImpl enumSyntaxImpl = new EnumSyntaxImpl(oid, property.getValue());
                    implementation(enumSyntaxImpl);
                    return getSchemaBuilder().addSyntax(new Syntax(this), overwrite)
                                             .buildMatchingRule(enumSyntaxImpl.getOrderingMatchingRule())
                                             .description(getDescription() + " enumeration ordering matching rule")
                                             .syntaxOID(oid)
                                             .extraProperties(CoreSchemaImpl.OPENDS_ORIGIN)
                                             .implementation(new EnumOrderingMatchingRule(enumSyntaxImpl))
                                             .addToSchemaOverwrite();
                }
            }
            return getSchemaBuilder().addSyntax(new Syntax(this), overwrite);
        }
        @Override
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
@@ -26,6 +26,7 @@
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.regex.Pattern;
import org.forgerock.i18n.LocalizableMessageBuilder;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
@@ -40,6 +41,7 @@
import org.forgerock.opendj.ldap.requests.SearchRequest;
import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.SearchResultEntry;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/** Test SchemaBuilder. */
@@ -2158,4 +2160,169 @@
                .as("Value should have been valid, but it is not: " + invalidReason.toString())
                .isTrue();
    }
    @DataProvider
    private Object[][] schemasWithEnumerationSyntaxes() {
        final Schema coreSchema = Schema.getCoreSchema();
        final Schema usingAddEnumerationSyntax = new SchemaBuilder("usingAddEnumerationSyntax")
                .addSchema(coreSchema, true)
                .addEnumerationSyntax("3.3.3", "Primary colors", true, "red", "green", "blue")
                .toSchema();
        final Schema usingAddSyntaxString = new SchemaBuilder("usingAddSyntaxString")
                .addSchema(coreSchema, true)
                .addSyntax("( 3.3.3  DESC 'Primary colors' X-ENUM ( 'red' 'green' 'blue' ))", true)
                .toSchema();
        final Schema usingBuildSyntaxCopy = new SchemaBuilder("usingBuildSyntaxCopy")
                .addSchema(coreSchema, true)
                .buildSyntax(usingAddEnumerationSyntax.getSyntax("3.3.3")).addToSchema()
                .toSchema();
        final Schema usingBuildSyntaxIncremental = new SchemaBuilder("usingBuildSyntaxIncremental")
                .addSchema(coreSchema, true)
                .buildSyntax("3.3.3")
                .description("Primary colors")
                .extraProperties("X-ENUM", "red", "green", "blue")
                .addToSchema()
                .toSchema();
        final Schema usingCopyOfSchema = new SchemaBuilder("usingCopyOfSchema")
                .addSchema(usingAddEnumerationSyntax, true)
                .toSchema();
        return new Object[][] {
            { usingAddEnumerationSyntax },
            { usingAddSyntaxString },
            { usingBuildSyntaxCopy },
            { usingBuildSyntaxIncremental },
            { usingCopyOfSchema }
        };
    }
    @Test(dataProvider = "schemasWithEnumerationSyntaxes")
    public void enumerationSyntaxesCanBeCreatedUsingDifferentApproaches(Schema schema) {
        assertThat(schema.getWarnings()).isEmpty();
        assertThat(schema.getMatchingRules())
                .as("Expected an enum ordering matching rule to be added for the enum syntax")
                .hasSize(Schema.getCoreSchema().getMatchingRules().size() + 1);
        LocalizableMessageBuilder msgBuilder = new LocalizableMessageBuilder();
        Syntax primaryColors = schema.getSyntax("3.3.3");
        assertThat(primaryColors.valueIsAcceptable(ByteString.valueOfUtf8("red"), msgBuilder)).isTrue();
        assertThat(primaryColors.valueIsAcceptable(ByteString.valueOfUtf8("yellow"), msgBuilder)).isFalse();
        MatchingRule matchingRule = schema.getMatchingRule(OMR_OID_GENERIC_ENUM + "." + "3.3.3");
        assertThat(matchingRule).isNotNull();
        assertThat(matchingRule).isSameAs(primaryColors.getOrderingMatchingRule());
    }
    @DataProvider
    private Object[][] schemasWithSubstitutionSyntaxes() {
        final Schema coreSchema = Schema.getCoreSchema();
        final Schema usingAddSubstitutionSyntax = new SchemaBuilder("usingAddSubstitutionSyntax")
                .addSchema(coreSchema, true)
                .addSubstitutionSyntax("3.3.3", "Long Integer", "1.3.6.1.4.1.1466.115.121.1.27", true)
                .toSchema();
        final Schema usingAddSyntaxString = new SchemaBuilder("usingAddSyntaxString")
                .addSchema(coreSchema, true)
                .addSyntax("( 3.3.3  DESC 'Long Integer' X-SUBST '1.3.6.1.4.1.1466.115.121.1.27' )", true)
                .toSchema();
        final Schema usingBuildSyntaxCopy = new SchemaBuilder("usingBuildSyntaxCopy")
                .addSchema(coreSchema, true)
                .buildSyntax(usingAddSubstitutionSyntax.getSyntax("3.3.3")).addToSchema()
                .toSchema();
        final Schema usingBuildSyntaxIncremental = new SchemaBuilder("usingBuildSyntaxIncremental")
                .addSchema(coreSchema, true)
                .buildSyntax("3.3.3")
                .description("Long Integer")
                .extraProperties("X-SUBST", "1.3.6.1.4.1.1466.115.121.1.27")
                .addToSchema()
                .toSchema();
        final Schema usingCopyOfSchema = new SchemaBuilder("usingCopyOfSchema")
                .addSchema(usingAddSubstitutionSyntax, true)
                .toSchema();
        return new Object[][] {
            { usingAddSubstitutionSyntax },
            { usingAddSyntaxString },
            { usingBuildSyntaxCopy },
            { usingBuildSyntaxIncremental },
            { usingCopyOfSchema }
        };
    }
    @Test(dataProvider = "schemasWithSubstitutionSyntaxes")
    public void substitutionSyntaxesCanBeCreatedUsingDifferentApproaches(Schema schema) {
        assertThat(schema.getWarnings()).isEmpty();
        LocalizableMessageBuilder msgBuilder = new LocalizableMessageBuilder();
        Syntax longInteger = schema.getSyntax("3.3.3");
        assertThat(longInteger.valueIsAcceptable(ByteString.valueOfUtf8("12345"), msgBuilder)).isTrue();
        assertThat(longInteger.valueIsAcceptable(ByteString.valueOfUtf8("NaN"), msgBuilder)).isFalse();
        MatchingRule matchingRule = schema.getMatchingRule("integerOrderingMatch");
        assertThat(matchingRule).isNotNull();
        assertThat(matchingRule).isSameAs(longInteger.getOrderingMatchingRule());
    }
    @DataProvider
    private Object[][] schemasWithPatternSyntaxes() {
        final Schema coreSchema = Schema.getCoreSchema();
        final Schema usingAddPatternSyntax = new SchemaBuilder("usingAddSubstitutionSyntax")
                .addSchema(coreSchema, true)
                .addPatternSyntax("3.3.3", "Host and Port", Pattern.compile("[^:]+:\\d+"), true)
                .toSchema();
        final Schema usingAddSyntaxString = new SchemaBuilder("usingAddSyntaxString")
                .addSchema(coreSchema, true)
                .addSyntax("( 3.3.3  DESC 'Host and Port' X-PATTERN '[^:]+:\\d+' )", true)
                .toSchema();
        final Schema usingBuildSyntaxCopy = new SchemaBuilder("usingBuildSyntaxCopy")
                .addSchema(coreSchema, true)
                .buildSyntax(usingAddPatternSyntax.getSyntax("3.3.3")).addToSchema()
                .toSchema();
        final Schema usingBuildSyntaxIncremental = new SchemaBuilder("usingBuildSyntaxIncremental")
                .addSchema(coreSchema, true)
                .buildSyntax("3.3.3")
                .description("Host and Port")
                .extraProperties("X-PATTERN", "[^:]+:\\d+")
                .addToSchema()
                .toSchema();
        final Schema usingCopyOfSchema = new SchemaBuilder("usingCopyOfSchema")
                .addSchema(usingAddPatternSyntax, true)
                .toSchema();
        return new Object[][] {
            { usingAddPatternSyntax },
            { usingAddSyntaxString },
            { usingBuildSyntaxCopy },
            { usingBuildSyntaxIncremental },
            { usingCopyOfSchema }
        };
    }
    @Test(dataProvider = "schemasWithPatternSyntaxes")
    public void patternSyntaxesCanBeCreatedUsingDifferentApproaches(Schema schema) {
        assertThat(schema.getWarnings()).isEmpty();
        LocalizableMessageBuilder msgBuilder = new LocalizableMessageBuilder();
        Syntax longInteger = schema.getSyntax("3.3.3");
        assertThat(longInteger.valueIsAcceptable(ByteString.valueOfUtf8("localhost:389"), msgBuilder)).isTrue();
        assertThat(longInteger.valueIsAcceptable(ByteString.valueOfUtf8("bad"), msgBuilder)).isFalse();
        MatchingRule matchingRule = schema.getMatchingRule("caseIgnoreOrderingMatch");
        assertThat(matchingRule).isNotNull();
        assertThat(matchingRule).isSameAs(longInteger.getOrderingMatchingRule());
    }
}
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java
@@ -11,7 +11,7 @@
 * Header, with the fields enclosed by brackets [] replaced by your own identifying
 * information: "Portions Copyright [year] [name of copyright owner]".
 *
 * Copyright 2014 ForgeRock AS.
 * Copyright 2014-2016 ForgeRock AS.
 * Portions Copyright 2014 Manuel Gaupp
 */
@@ -30,34 +30,32 @@
public class SyntaxTestCase extends AbstractSchemaTestCase {
    @Test
    public final void testCreatesANewSyntax() {
    public final void testBuilderCreatesCustomSyntax() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
            .description("Security Label")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new DirectoryStringSyntaxImpl())
            .addToSchema()
                .description("Security Label")
                .extraProperties("X-TEST", "1", "2", "3")
                .implementation(new DirectoryStringSyntaxImpl())
                .addToSchema()
            .toSchema();
        // @formatter:on
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getExtraProperties().get("X-TEST")).hasSize(3);
        assertThat(syntax.getApproximateMatchingRule().getNameOrOID()).isEqualTo("ds-mr-double-metaphone-approx");
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreSubstringsMatch");
        assertThat(syntax.toString()).isEqualTo(
                "( 1.9.1.2.3 DESC 'Security Label' X-ENUM ( 'top-secret' 'secret' 'confidential' ) )");
        assertThat(syntax.toString()).isEqualTo("( 1.9.1.2.3 DESC 'Security Label' X-TEST ( '1' '2' '3' ) )");
        assertThat(syntax.isHumanReadable()).isTrue();
        assertThat(syntax.isBEREncodingRequired()).isFalse();
    }
    /**
     * Tests that unrecognized syntaxes are automatically substituted with the
     * default syntax during building.
     * Tests that unrecognized syntaxes are automatically substituted with the default syntax during building.
     */
    @Test
    public final void testBuilderSubstitutesUnknownSyntaxWithDefaultSyntax() {
@@ -70,14 +68,12 @@
        assertThat(syntax.getDescription()).isEmpty();
        assertThat(syntax.getApproximateMatchingRule()).isNull();
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo(
                "octetStringOrderingMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isNull();
    }
    /**
     * Tests that unrecognized syntaxes are automatically substituted with the
     * default syntax and matching rule.
     * Tests that unrecognized syntaxes are automatically substituted with the default syntax and matching rule.
     */
    @Test
    public final void testDefaultSyntaxSubstitution() {
@@ -85,12 +81,10 @@
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEmpty();
        // Dynamically created syntaxes include the X-SUBST extension.
        assertThat(syntax.getExtraProperties().get("X-SUBST").get(0)).isEqualTo(
                "1.3.6.1.4.1.1466.115.121.1.40");
        assertThat(syntax.getExtraProperties().get("X-SUBST").get(0)).isEqualTo("1.3.6.1.4.1.1466.115.121.1.40");
        assertThat(syntax.getApproximateMatchingRule()).isNull();
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo(
                "octetStringOrderingMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isNull();
    }
@@ -99,14 +93,7 @@
     */
    @Test(expectedExceptions = IllegalArgumentException.class)
    public final void testBuilderDoesNotAllowEmptyOid() {
        // @formatter:off
        new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("")
            .description("Security Label")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new DirectoryStringSyntaxImpl())
            .addToSchema();
        // @formatter:on
        new SchemaBuilder(Schema.getCoreSchema()).buildSyntax("").addToSchema();
    }
    /**
@@ -114,14 +101,7 @@
     */
    @Test(expectedExceptions = IllegalArgumentException.class)
    public final void testBuilderDoesNotAllowNullOid() {
        // @formatter:off
        new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax((String) null)
            .description("Security Label")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new DirectoryStringSyntaxImpl())
            .addToSchema();
        // @formatter:on
        new SchemaBuilder(Schema.getCoreSchema()).buildSyntax((String) null).addToSchema();
    }
    /**
@@ -129,28 +109,19 @@
     */
    @Test
    public final void testBuilderAllowsNullSyntax() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
                .implementation(null)
                .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").implementation(null).addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isNotEmpty();
        assertThat(schema.getWarnings().toString()).contains("It will be substituted by the default syntax");
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
        assertThat(syntax.toString()).isEqualTo(
                "( 1.9.1.2.3 DESC 'Security Label' X-ENUM ( 'top-secret' 'secret' 'confidential' ) )");
        assertThat(syntax.toString()).isEqualTo("( 1.9.1.2.3 )");
    }
    /**
@@ -158,27 +129,19 @@
     */
    @Test
    public final void testBuilderAllowsNoSyntax() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
                .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isNotEmpty();
        assertThat(schema.getWarnings().toString()).contains("It will be substituted by the default syntax");
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
        assertThat(syntax.toString()).isEqualTo(
                "( 1.9.1.2.3 DESC 'Security Label' X-ENUM ( 'top-secret' 'secret' 'confidential' ) )");
        assertThat(syntax.toString()).isEqualTo("( 1.9.1.2.3 )");
    }
    /**
@@ -187,28 +150,20 @@
     */
    @Test
    public final void testBuilderAllowsNoSyntaxCaseWhereDefaultSyntaxIsChanged() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .setOption(DEFAULT_SYNTAX_OID, "1.3.6.1.4.1.1466.115.121.1.15")
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
                .addToSchema()
            .toSchema();
        // @formatter:on
                .setOption(DEFAULT_SYNTAX_OID, "1.3.6.1.4.1.1466.115.121.1.15")
                .buildSyntax("1.9.1.2.3").addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isNotEmpty();
        assertThat(schema.getWarnings().toString()).contains("It will be substituted by the default syntax");
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule().getNameOrOID()).isEqualTo("ds-mr-double-metaphone-approx");
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreSubstringsMatch");
        assertThat(syntax.toString()).isEqualTo(
                "( 1.9.1.2.3 DESC 'Security Label' X-ENUM ( 'top-secret' 'secret' 'confidential' ) )");
        assertThat(syntax.toString()).isEqualTo("( 1.9.1.2.3 )");
    }
    /**
@@ -216,24 +171,13 @@
     */
    @Test
    public final void testBuilderAllowsNoDescription() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new OctetStringSyntaxImpl())
            .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
@@ -241,25 +185,13 @@
     */
    @Test
    public final void testBuilderAllowsNullDescription() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
            .description(null)
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new OctetStringSyntaxImpl())
            .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").description(null).addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
@@ -267,25 +199,13 @@
     */
    @Test
    public final void testBuilderAllowsEmptyDescription() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
            .description("")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new OctetStringSyntaxImpl())
            .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").description("").addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
@@ -293,24 +213,13 @@
     */
    @Test
    public final void testBuilderAllowsNoExtraProperties() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .implementation(new OctetStringSyntaxImpl())
                .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3").addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().isEmpty()).isTrue();
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
@@ -318,15 +227,7 @@
     */
    @Test(expectedExceptions = NullPointerException.class)
    public final void testBuilderDoesNotAllowNullExtraProperties() {
        // @formatter:off
        new SchemaBuilder(Schema.getCoreSchema())
                .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties(null)
                .implementation(new OctetStringSyntaxImpl())
                .addToSchema()
            .toSchema();
        // @formatter:on
        new SchemaBuilder(Schema.getCoreSchema()).buildSyntax("1.9.1.2.3").extraProperties(null);
    }
    /**
@@ -334,26 +235,14 @@
     */
    @Test
    public final void testBuilderRemoveExtraProperties() {
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
                .implementation(new OctetStringSyntaxImpl())
                .removeAllExtraProperties()
                .addToSchema()
            .toSchema();
        // @formatter:on
                .buildSyntax("1.9.1.2.3")
                .extraProperties("X-ENUM", "1", "2", "3").removeAllExtraProperties().addToSchema()
                .toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().isEmpty()).isTrue();
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
@@ -364,29 +253,20 @@
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
                .description("Security Label")
                .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
                .extraProperties("X-ORIGIN", "Sam Carter")
                .implementation(new OctetStringSyntaxImpl())
                .removeExtraProperty("X-ENUM", "top-secret")
                .addToSchema()
            .toSchema();
        // @formatter:on
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax).isNotNull();
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().isEmpty()).isFalse();
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(2);
        assertThat(syntax.getExtraProperties().get("X-ORIGIN").size()).isEqualTo(1);
        assertThat(syntax.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(syntax.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(syntax.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");
        assertThat(syntax.getSubstringMatchingRule()).isEqualTo(null);
    }
    /**
     * Sets a syntax using a string definition.
     */
@@ -470,41 +350,20 @@
     */
    @Test
    public final void testBuilderDuplicatesExistingSyntax() {
        final SchemaBuilder sb = new SchemaBuilder();
        sb.addSchema(Schema.getCoreSchema(), false);
        final Schema schema1 = new SchemaBuilder(Schema.getCoreSchema())
                .buildSyntax("1.2.3.4.5.6").description("v1").addToSchema()
                .toSchema();
        // @formatter:off
        final Syntax.Builder syntaxBuilder = new Syntax.Builder("1.9.1.2.3", sb);
        syntaxBuilder.description("Security Label")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new DirectoryStringSyntaxImpl())
            .addToSchema();
        // @formatter:on
        final Syntax syntax1 = schema1.getSyntax("1.2.3.4.5.6");
        assertThat(syntax1.getDescription()).isEqualTo("v1");
        Schema schema = sb.toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax syntax = schema.getSyntax("1.9.1.2.3");
        assertThat(syntax.getDescription()).isEqualTo("Security Label");
        assertThat(syntax.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        final Schema schema2 = new SchemaBuilder(Schema.getCoreSchema())
                .buildSyntax(syntax1).description("v2").addToSchema()
                .toSchema();
        // @formatter:off
        sb.buildSyntax(syntax)
            .description("Security Label II")
            .extraProperties("X-ENUM", "private")
            .addToSchemaOverwrite();
        // @formatter:on
        schema = sb.toSchema();
        assertThat(schema.getWarnings()).isEmpty();
        final Syntax dolly = schema.getSyntax("1.9.1.2.3");
        assertThat(dolly.getDescription()).isEqualTo("Security Label II");
        assertThat(dolly.getExtraProperties().get("X-ENUM").size()).isEqualTo(4);
        assertThat(dolly.getApproximateMatchingRule().getNameOrOID()).isEqualTo("ds-mr-double-metaphone-approx");
        assertThat(dolly.getEqualityMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreMatch");
        assertThat(dolly.getOrderingMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreOrderingMatch");
        assertThat(dolly.getSubstringMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreSubstringsMatch");
        assertThat(dolly.toString()).isEqualTo(
                "( 1.9.1.2.3 DESC 'Security Label II' X-ENUM ( 'top-secret' 'secret' 'confidential' 'private' ) )");
        final Syntax syntax2 = schema2.getSyntax("1.2.3.4.5.6");
        assertThat(syntax2.getDescription()).isEqualTo("v2");
        assertThat(syntax2.toString()).isEqualTo("( 1.2.3.4.5.6 DESC 'v2' )");
    }
    /**
@@ -673,24 +532,24 @@
        // @formatter:off
        final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
            .buildSyntax("1.9.1.2.3")
            .description("Security Label")
            .extraProperties("X-ENUM", "top-secret", "secret", "confidential")
            .implementation(new DirectoryStringSyntaxImpl())
            .addToSchema()
                .description("Security Label")
                .extraProperties("X-TEST", "1", "2", "3")
                .implementation(new DirectoryStringSyntaxImpl())
                .addToSchema()
            .buildSyntax("1.9.1.2.4")
            .description("Security Label II")
            .extraProperties("X-ENUM", "private")
            .addToSchema()
                .description("Security Label II")
                .extraProperties("X-TEST", "private")
                .addToSchema()
            .buildSyntax("non-implemented-syntax-oid")
            .description("Not Implemented in OpenDJ")
            .extraProperties("X-SUBST", "1.3.6.1.4.1.1466.115.121.1.15")
            .implementation(null)
            .addToSchema()
                .description("Not Implemented in OpenDJ")
                .extraProperties("X-SUBST", "1.3.6.1.4.1.1466.115.121.1.15")
                .implementation(null)
                .addToSchema()
            .buildSyntax("1.3.6.1.4.1.4203.1.1.2")
            .description("Authentication Password Syntax")
            .extraProperties("X-ORIGIN", "RFC 4512")
            .implementation(new OctetStringSyntaxImpl())
            .addToSchemaOverwrite()
                .description("Authentication Password Syntax")
                .extraProperties("X-ORIGIN", "RFC 4512")
                .implementation(new OctetStringSyntaxImpl())
                .addToSchemaOverwrite()
            .toSchema();
        // @formatter:on
@@ -706,12 +565,12 @@
        assertThat(s1.getEqualityMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreMatch");
        assertThat(s1.getOrderingMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreOrderingMatch");
        assertThat(s1.getSubstringMatchingRule().getNameOrOID()).isEqualTo("caseIgnoreSubstringsMatch");
        assertThat(s1.getExtraProperties().get("X-ENUM").size()).isEqualTo(3);
        assertThat(s1.getExtraProperties().get("X-TEST")).hasSize(3);
        // Second
        final Syntax s2 = schema.getSyntax("1.9.1.2.4");
        assertThat(s2.getDescription()).isEqualTo("Security Label II");
        assertThat(s2.getExtraProperties().get("X-ENUM").size()).isEqualTo(1);
        assertThat(s2.getExtraProperties().get("X-TEST")).hasSize(1);
        assertThat(s2.getApproximateMatchingRule()).isEqualTo(null);
        assertThat(s2.getEqualityMatchingRule().getNameOrOID()).isEqualTo("octetStringMatch");
        assertThat(s2.getOrderingMatchingRule().getNameOrOID()).isEqualTo("octetStringOrderingMatch");