From 7fa23acfcc79bad31f121cecdba4eaa8d702595e Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Wed, 26 Nov 2014 10:30:37 +0000
Subject: [PATCH] [OPENDJ-1478] CR-5392 Make it easier to add compatibility options to schemas

---
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java                             |   55 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java                                          |  482 ++++------------
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java                                  |    3 
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java                                  |   22 
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java                                                  |   31 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java                                            |    8 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java                               |   49 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaOptions.java                                          |  214 +++++++
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java                              |    8 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java                                |   16 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java                                  |   56 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java                                |   61 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java                                          |   13 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java                        |   54 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java                                                 |  204 +-----
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java                                         |    7 
 opendj-sdk/opendj-core/clirr-ignored-api-changes.xml                                                                              |    6 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Option.java                                                        |   91 +++
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java                                        |   14 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java                                          |    5 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java                                         |    3 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java                              |   58 -
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaOptionsTestCase.java                                  |   83 ++
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java                                 |   53 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java               |    7 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java                              |    3 
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java                                       |   38 
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxTest.java                                  |    7 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java                                     |   64 -
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java |   14 
 30 files changed, 853 insertions(+), 876 deletions(-)

diff --git a/opendj-sdk/opendj-core/clirr-ignored-api-changes.xml b/opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
index 8b7b89b..ac802e6 100644
--- a/opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
+++ b/opendj-sdk/opendj-core/clirr-ignored-api-changes.xml
@@ -390,4 +390,10 @@
     <to>*toIrreversibleNormalizedByteString()</to>
     <justification>OPENDJ-1585 Function has been renamed to avoid abuse</justification>
   </difference>
+  <difference>
+    <className>%regex[org/forgerock/opendj/ldap/schema/Schema(Builder)?]</className>
+    <differenceType>7002</differenceType>
+    <method>%regex[(boolean|org.forgerock.opendj.ldap.schema.SchemaBuilder) allow(.)*\((boolean)?\)]</method>
+    <justification>OPENDJ-1478 Make it easier to add compatibility options to schemas</justification>
+  </difference>
 </differences>
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
index 92f1b48..5afa4c0 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/AttributeDescription.java
@@ -27,9 +27,6 @@
 
 package org.forgerock.opendj.ldap;
 
-import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-
 import java.util.Arrays;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -50,6 +47,11 @@
 import com.forgerock.opendj.util.ASCIICharProp;
 import com.forgerock.opendj.util.Iterators;
 
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
+
 /**
  * An attribute description as defined in RFC 4512 section 2.5. Attribute
  * descriptions are used to identify an attribute in an entry and are composed
@@ -740,9 +742,8 @@
     }
 
     /** Uncached valueOf implementation. */
-    private static AttributeDescription valueOf0(final String attributeDescription,
-            final Schema schema) {
-        final boolean allowMalformedNamesAndOptions = schema.allowMalformedNamesAndOptions();
+    private static AttributeDescription valueOf0(final String attributeDescription, final Schema schema) {
+        final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
         int i = 0;
         final int length = attributeDescription.length();
         char c = 0;
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Option.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Option.java
new file mode 100644
index 0000000..58c4609
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/Option.java
@@ -0,0 +1,91 @@
+/*
+ * 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 2014 ForgeRock AS
+ */
+package org.forgerock.opendj.ldap;
+
+/**
+ * Public API for defining new options.
+ *
+ * @param <T> The option type.
+ */
+public final class Option<T> {
+    private final Class<T> type;
+    private final T defaultValue;
+
+    /**
+     * Defines a new {@link Boolean} option with the provided default value.
+     *
+     * @param defaultValue
+     *            The {@link Boolean} default value of this option.
+     * @return A new {@link Boolean} option with the provided default value.
+     */
+    public static Option<Boolean> withDefault(boolean defaultValue) {
+        return of(Boolean.class, defaultValue);
+    }
+
+    /**
+     * Defines a new option of the provided type with the provided default
+     * value.
+     *
+     * @param <T>
+     *            The type of this option.
+     * @param type
+     *            The type of the option.
+     * @param defaultValue
+     *            The new option default value.
+     * @return A new option of the provided type with the provided default
+     *         value.
+     */
+    public static <T> Option<T> of(Class<T> type, T defaultValue) {
+        return new Option<T>(type, defaultValue);
+    }
+
+    private Option(Class<T> type, T defaultValue) {
+        this.type = type;
+        this.defaultValue = defaultValue;
+    }
+
+    /**
+     * Returns the type of this option.
+     *
+     * @return the type of this option.
+     */
+    public Class<T> getType() {
+        return type;
+    }
+
+    /**
+     * Returns the provided value if not {@code null}, otherwise returns the
+     * default one.
+     *
+     * @param value
+     *            The option which overrides the default one if not null.
+     * @return The provided value if not {@code null}, otherwise return the
+     *         default one.
+     */
+    public T getValue(Object value) {
+        return value != null ? type.cast(value) : defaultValue;
+    }
+}
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java
index bc94767..5893ce3 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeTypeSyntaxImpl.java
@@ -26,10 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
-
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -37,6 +33,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class defines the attribute type description syntax, which is used to
  * hold attribute type definitions in the server schema. The format of this
@@ -51,43 +53,39 @@
         return EMR_OID_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_ATTRIBUTE_TYPE_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
 
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         final String definition = value.toString();
         try {
             final SubstringReader reader = new SubstringReader(definition);
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
 
             // We'll do this a character at a time. First, skip over any
             // leading whitespace.
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the definition was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRTYPE_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_ATTRTYPE_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final DecodeException e = DecodeException.error(
-                    ERR_ATTR_SYNTAX_ATTRTYPE_EXPECTED_OPEN_PARENTHESIS.get(
-                        definition, reader.pos() - 1, String.valueOf(c)));
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_ATTRTYPE_EXPECTED_OPEN_PARENTHESIS.get(
+                    definition, reader.pos() - 1, String.valueOf(c)));
             }
 
             // Skip over any spaces immediately following the opening
@@ -95,7 +93,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            readOID(reader, allowMalformedNamesAndOptions);
 
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
@@ -112,12 +110,12 @@
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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
@@ -126,19 +124,19 @@
                     // This specifies the name or OID of the superior attribute
                     // type from which this attribute type should inherit its
                     // properties.
-                    SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    readOID(reader, allowMalformedNamesAndOptions);
                 } else if ("equality".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the equality matching
                     // rule to use for this attribute type.
-                    SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    readOID(reader, allowMalformedNamesAndOptions);
                 } else if ("ordering".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the ordering matching
                     // rule to use for this attribute type.
-                    SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    readOID(reader, allowMalformedNamesAndOptions);
                 } else if ("substr".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the substring matching
                     // rule to use for this attribute type.
-                    SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    readOID(reader, allowMalformedNamesAndOptions);
                 } else if ("syntax".equalsIgnoreCase(tokenName)) {
                     // This specifies the numeric OID of the syntax for this
                     // matching rule. It may optionally be immediately followed
@@ -150,7 +148,7 @@
                     // does
                     // not impose any practical limit on the length of attribute
                     // values.
-                    SchemaUtils.readOIDLen(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDLen(reader, allowMalformedNamesAndOptions);
                 } else if ("single-definition".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are allowed to
                     // have at most one definition. We do not need any more
@@ -187,12 +185,8 @@
                             && !"directoryoperation".equalsIgnoreCase(usageStr)
                             && !"distributedoperation".equalsIgnoreCase(usageStr)
                             && !"dsaoperation".equalsIgnoreCase(usageStr)) {
-                        final LocalizableMessage message =
-                                WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition,
-                                        usageStr);
-                        final DecodeException e = DecodeException.error(message);
-                        logger.debug(LocalizableMessage.raw("%s", e));
-                        throw e;
+                        throwDecodeException(logger,
+                            WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition, usageStr));
                     }
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
@@ -202,11 +196,8 @@
                     // parenthesis.
                     SchemaUtils.readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_TOKEN1.get(definition, tokenName));
+
                 }
             }
             return true;
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java
index 765575d..ec1ea87 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxImpl.java
@@ -38,6 +38,7 @@
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_CERTIFICATE_EXACT_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_OCTET_STRING_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_CERTIFICATE_NAME;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 import static org.forgerock.opendj.io.ASN1.*;
 
 import com.forgerock.opendj.util.StaticUtils;
@@ -89,7 +90,7 @@
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // Skip validation if strict validation is disabled.
-        if (schema.allowMalformedCertificates()) {
+        if (schema.getOption(ALLOW_MALFORMED_CERTIFICATES)) {
             return true;
         }
 
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java
index e57d3ff..9881561 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITContentRuleSyntaxImpl.java
@@ -26,10 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
-
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -37,6 +33,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the DIT content rule description syntax, which is used
  * to hold DIT content rule definitions in the server schema. The format of this
@@ -75,28 +77,24 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_DCR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final DecodeException e = DecodeException.error(
-                        ERR_ATTR_SYNTAX_DCR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_DCR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
             // parenthesis.
             reader.skipWhitespaces();
 
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            readOID(reader, allowMalformedNamesAndOptions);
 
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
@@ -107,49 +105,44 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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.
                 } else if ("aux".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("not".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } 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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_DCR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_DCR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_DCR_INVALID1
-                    .get(definition, de.getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_DCR_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java
index a01170c..fbde434 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DITStructureRuleSyntaxImpl.java
@@ -26,10 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
-
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -37,6 +33,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the DIT structure rule description syntax, which is
  * used to hold DIT structure rule definitions in the server schema. The format
@@ -51,14 +53,17 @@
         return EMR_INTEGER_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_DIT_STRUCTURE_RULE_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
 
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // We'll use the decodeDITStructureRule method to determine if the
@@ -72,22 +77,16 @@
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the value was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_DSR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final DecodeException e = DecodeException.error(
-                        ERR_ATTR_SYNTAX_DSR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_DSR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -95,7 +94,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readRuleID(reader);
+            readRuleID(reader);
 
             String nameForm = null;
 
@@ -108,52 +107,44 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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.
                 } else if ("form".equalsIgnoreCase(tokenName)) {
-                    nameForm = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    nameForm = readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readRuleIDs(reader);
+                    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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_DSR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_DSR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             if (nameForm == null) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(definition));
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_DSR_INVALID1
-                    .get(definition, de.getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java
index cec5d2a..d318208 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DirectoryStringSyntaxImpl.java
@@ -29,6 +29,7 @@
 
 import static com.forgerock.opendj.ldap.CoreMessages.ERR_ATTR_SYNTAX_DIRECTORYSTRING_INVALID_ZEROLENGTH_VALUE;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -84,11 +85,10 @@
      */
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
-        if (value.length() > 0 || schema.allowZeroLengthDirectoryStrings()) {
+        if (value.length() > 0 || schema.getOption(ALLOW_ZERO_LENGTH_DIRECTORY_STRINGS)) {
             return true;
-        } else {
-            invalidReason.append(ERR_ATTR_SYNTAX_DIRECTORYSTRING_INVALID_ZEROLENGTH_VALUE.get());
-            return false;
         }
+        invalidReason.append(ERR_ATTR_SYNTAX_DIRECTORYSTRING_INVALID_ZEROLENGTH_VALUE.get());
+        return false;
     }
 }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java
index 83d3e5d..d873a98 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/EnhancedGuideSyntaxImpl.java
@@ -29,9 +29,12 @@
 
 import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
 import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OCTET_STRING_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_OCTET_STRING_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_ENHANCED_GUIDE_NAME;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -50,6 +53,7 @@
         return EMR_OCTET_STRING_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_ENHANCED_GUIDE_NAME;
     }
@@ -59,6 +63,7 @@
         return OMR_OCTET_STRING_OID;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
@@ -77,8 +82,9 @@
      * @return <CODE>true</CODE> if the provided value is acceptable for use
      *         with this syntax, or <CODE>false</CODE> if not.
      */
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
-            final LocalizableMessageBuilder invalidReason) {
+        final LocalizableMessageBuilder invalidReason) {
         // Get a lowercase string version of the provided value.
         final String valueStr = toLowerCase(value.toString());
 
@@ -99,8 +105,8 @@
         }
 
         try {
-            SchemaUtils.readOID(new SubstringReader(ocName.substring(ocLength)), schema
-                    .allowMalformedNamesAndOptions());
+            readOID(new SubstringReader(ocName.substring(ocLength)),
+                schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
         } catch (final DecodeException de) {
             invalidReason.append(de.getMessageObject());
             return false;
@@ -116,12 +122,12 @@
 
         final String scopeStr = valueStr.substring(lastSharpPos + 1).trim();
         if (!"baseobject".equals(scopeStr) && !"onelevel".equals(scopeStr)
-                && !"wholesubtree".equals(scopeStr) && !"subordinatesubtree".equals(scopeStr)) {
+            && !"wholesubtree".equals(scopeStr) && !"subordinatesubtree".equals(scopeStr)) {
             if (scopeStr.length() == 0) {
                 invalidReason.append(ERR_ATTR_SYNTAX_ENHANCEDGUIDE_NO_SCOPE.get(valueStr));
             } else {
                 invalidReason.append(ERR_ATTR_SYNTAX_ENHANCEDGUIDE_INVALID_SCOPE.get(valueStr,
-                        scopeStr));
+                    scopeStr));
             }
             return false;
         }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java
index 0155d03..432299a 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/GuideSyntaxImpl.java
@@ -29,9 +29,12 @@
 
 import static com.forgerock.opendj.util.StaticUtils.toLowerCase;
 import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OCTET_STRING_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.OMR_OCTET_STRING_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_GUIDE_NAME;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -180,8 +183,8 @@
             return false;
         } else {
             try {
-                SchemaUtils.readOID(new SubstringReader(criteria.substring(0, dollarPos)), schema
-                        .allowMalformedNamesAndOptions());
+                readOID(new SubstringReader(criteria.substring(0, dollarPos)),
+                    schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
             } catch (final DecodeException de) {
                 invalidReason.append(de.getMessageObject());
                 return false;
@@ -272,6 +275,7 @@
         return EMR_OCTET_STRING_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_GUIDE_NAME;
     }
@@ -281,6 +285,7 @@
         return OMR_OCTET_STRING_OID;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
@@ -299,6 +304,7 @@
      * @return <CODE>true</CODE> if the provided value is acceptable for use
      *         with this syntax, or <CODE>false</CODE> if not.
      */
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // Get a lowercase string version of the provided value.
@@ -320,8 +326,8 @@
         }
 
         try {
-            SchemaUtils.readOID(new SubstringReader(ocName.substring(0, ocLength)), schema
-                    .allowMalformedNamesAndOptions());
+            readOID(new SubstringReader(ocName.substring(0, ocLength)),
+                    schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
         } catch (final DecodeException de) {
             invalidReason.append(de.getMessageObject());
             return false;
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
index a66b7bc..3435865 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/JPEGSyntaxImpl.java
@@ -30,6 +30,7 @@
 import org.forgerock.opendj.ldap.ByteSequence;
 
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 
 /**
  * This class implements the JPEG attribute syntax. This is actually
@@ -75,7 +76,7 @@
     @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
-        return schema.allowMalformedJPEGPhotos() || isValidJfif(value) || isValidExif(value);
+        return schema.getOption(ALLOW_MALFORMED_JPEG_PHOTOS) || isValidJfif(value) || isValidExif(value);
     }
 
     /**
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java
index 7d9a1d4..d9d9ca3 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/LDAPSyntaxDescriptionSyntaxImpl.java
@@ -26,9 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
-
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -36,7 +33,6 @@
 import java.util.Map;
 import java.util.regex.Pattern;
 
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -44,6 +40,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class defines the LDAP syntax description syntax, which is used to hold
  * attribute syntax definitions in the schema. The format of this syntax is
@@ -58,14 +60,17 @@
         return EMR_OID_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_LDAP_SYNTAX_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
 
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // We'll use the decodeNameForm method to determine if the value is
@@ -79,23 +84,16 @@
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the value was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final DecodeException e = DecodeException.error(
-                        ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_ATTRSYNTAX_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -103,7 +101,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String oid = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            final String oid = readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
 
             Map<String, List<String>> extraProperties = Collections.emptyMap();
             // At this point, we should have a pretty specific syntax that
@@ -135,11 +133,7 @@
                     }
                     extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
@@ -151,12 +145,8 @@
                         try {
                             Pattern.compile(values.next());
                         } catch (final Exception e) {
-                            final LocalizableMessage message =
-                                    WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN.get(oid,
-                                            pattern);
-                            final DecodeException de = DecodeException.error(message, e);
-                            logger.debug(LocalizableMessage.raw("%s", e));
-                            throw de;
+                            throwDecodeException(logger,
+                                WARN_ATTR_SYNTAX_LDAPSYNTAX_REGEX_INVALID_PATTERN.get(oid, pattern));
                         }
                         break;
                     }
@@ -166,11 +156,8 @@
                         final String entry = values.get(i);
                         for (int j = i + 1; j < values.size(); j++) {
                             if (entry.equals(values.get(j))) {
-                                final LocalizableMessage message =
-                                        WARN_ATTR_SYNTAX_LDAPSYNTAX_ENUM_DUPLICATE_VALUE.get(oid, entry, j);
-                                final DecodeException e = DecodeException.error(message);
-                                logger.debug(LocalizableMessage.raw("%s", e));
-                                throw e;
+                                throwDecodeException(logger,
+                                    WARN_ATTR_SYNTAX_LDAPSYNTAX_ENUM_DUPLICATE_VALUE.get(oid, entry, j));
                             }
                         }
                     }
@@ -179,8 +166,7 @@
 
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_INVALID1.get(definition, de
-                    .getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_ATTRSYNTAX_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java
index d3088bc..ac44474 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleSyntaxImpl.java
@@ -26,10 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
-
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -37,6 +33,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the matching rule description syntax, which is used to
  * hold matching rule definitions in the server schema. The format of this
@@ -51,10 +53,12 @@
         return EMR_OID_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_MATCHING_RULE_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
@@ -73,6 +77,7 @@
      * @return <CODE>true</CODE> if the provided value is acceptable for use
      *         with this syntax, or <CODE>false</CODE> if not.
      */
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // We'll use the decodeMatchingRule method to determine if the value
@@ -86,23 +91,16 @@
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the value was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MR_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_MR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_MR_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -110,7 +108,8 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+            readOID(reader, allowMalformedNamesAndOptions);
             String syntax = null;
 
             // At this point, we should have a pretty specific syntax that
@@ -128,46 +127,38 @@
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the matching rule. It
                     // is
                     // an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    readQuotedString(reader);
                 } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the matching rule should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                 } else if ("syntax".equalsIgnoreCase(tokenName)) {
-                    syntax = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    syntax = readOID(reader, allowMalformedNamesAndOptions);
                 } 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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_MR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_MR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that a syntax was specified.
             if (syntax == null) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MR_NO_SYNTAX.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_MR_NO_SYNTAX.get(definition));
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason
-                    .append(ERR_ATTR_SYNTAX_MR_INVALID1.get(definition, de.getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_MR_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java
index a7a004d..d65ca3f 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/MatchingRuleUseSyntaxImpl.java
@@ -26,13 +26,8 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OID_FIRST_COMPONENT_OID;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_MATCHING_RULE_USE_NAME;
-
 import java.util.Set;
 
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -40,6 +35,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the matching rule use description syntax, which is used
  * to hold matching rule use definitions in the server schema. The format of
@@ -54,10 +55,12 @@
         return EMR_OID_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_MATCHING_RULE_USE_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
@@ -76,6 +79,7 @@
      * @return <CODE>true</CODE> if the provided value is acceptable for use
      *         with this syntax, or <CODE>false</CODE> if not.
      */
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // We'll use the decodeAttributeType method to determine if the
@@ -89,24 +93,16 @@
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the value was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MRUSE_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_MRUSE_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MRUSE_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_MRUSE_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -114,7 +110,8 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+            readOID(reader, allowMalformedNamesAndOptions);
 
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
@@ -126,52 +123,43 @@
             // for everything else we might need to know.
             Set<String> attributes = null;
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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.
                 } else if ("applies".equalsIgnoreCase(tokenName)) {
-                    attributes =
-                            SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    attributes = readOIDs(reader, allowMalformedNamesAndOptions);
                 } 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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_MRUSE_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_MRUSE_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that the set of attributes was defined.
             if (attributes == null || attributes.size() == 0) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MRUSE_NO_ATTR.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_MRUSE_NO_ATTR.get(definition));
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, de
-                    .getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_MRUSE_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java
index 6d96cd2..bbd0828 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/NameFormSyntaxImpl.java
@@ -26,13 +26,8 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OID_FIRST_COMPONENT_OID;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_NAME_FORM_NAME;
-
 import java.util.Set;
 
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -40,6 +35,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the name form description syntax, which is used to hold
  * name form definitions in the server schema. The format of this syntax is
@@ -77,22 +78,15 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_NAME_FORM_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_NAME_FORM_EXPECTED_OPEN_PARENTHESIS.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -100,7 +94,8 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+            readOID(reader, allowMalformedNamesAndOptions);
 
             String structuralClass = null;
             Set<String> requiredAttributes = null;
@@ -114,67 +109,52 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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.
                 } else if ("oc".equalsIgnoreCase(tokenName)) {
-                    structuralClass =
-                            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+                    structuralClass = readOID(reader, allowMalformedNamesAndOptions);
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    requiredAttributes =
-                            SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    requiredAttributes = readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } 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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that a structural class was specified. If not, then
             // it cannot be valid.
             if (structuralClass == null) {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_NO_STRUCTURAL_CLASS1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_NAME_FORM_NO_STRUCTURAL_CLASS1.get(definition));
             }
 
             if (requiredAttributes == null || requiredAttributes.size() == 0) {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_NO_REQUIRED_ATTR.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_NAME_FORM_NO_REQUIRED_ATTR.get(definition));
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_NAME_FORM_INVALID1.get(definition, de
-                    .getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_NAME_FORM_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java
index 9dea244..6963ea0 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/OIDSyntaxImpl.java
@@ -30,6 +30,7 @@
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OID_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SMR_CASE_IGNORE_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_OID_NAME;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -77,8 +78,8 @@
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         try {
-            SchemaUtils.readOID(new SubstringReader(value.toString()), schema
-                    .allowMalformedNamesAndOptions());
+            SchemaUtils.readOID(new SubstringReader(value.toString()),
+                    schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
             return true;
         } catch (final DecodeException de) {
             invalidReason.append(de.getMessageObject());
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java
index 4be4182..38c7a2c 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClassSyntaxImpl.java
@@ -26,11 +26,6 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_OID_FIRST_COMPONENT_OID;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_OBJECTCLASS_NAME;
-
-import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
 import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
@@ -38,6 +33,12 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+
 /**
  * This class implements the object class description syntax, which is used to
  * hold objectclass definitions in the server schema. The format of this syntax
@@ -52,14 +53,17 @@
         return EMR_OID_FIRST_COMPONENT_OID;
     }
 
+    @Override
     public String getName() {
         return SYNTAX_OBJECTCLASS_NAME;
     }
 
+    @Override
     public boolean isHumanReadable() {
         return true;
     }
 
+    @Override
     public boolean valueIsAcceptable(final Schema schema, final ByteSequence value,
             final LocalizableMessageBuilder invalidReason) {
         // We'll use the decodeObjectClass method to determine if the value
@@ -73,24 +77,16 @@
             reader.skipWhitespaces();
 
             if (reader.remaining() <= 0) {
-                // This means that the value was empty or contained only
-                // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_OBJECTCLASS_EMPTY_VALUE1.get(definition);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                // Value was empty or contained only whitespace. This is illegal.
+                throwDecodeException(logger, ERR_ATTR_SYNTAX_OBJECTCLASS_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
             // then that is an error.
             final char c = reader.read();
             if (c != '(') {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_OBJECTCLASS_EXPECTED_OPEN_PARENTHESIS1.get(definition, reader.pos() - 1, c);
-                final DecodeException e = DecodeException.error(message);
-                logger.debug(LocalizableMessage.raw("%s", e));
-                throw e;
+                throwDecodeException(logger,
+                    ERR_ATTR_SYNTAX_OBJECTCLASS_EXPECTED_OPEN_PARENTHESIS1.get(definition, reader.pos() - 1, c));
             }
 
             // Skip over any spaces immediately following the opening
@@ -98,7 +94,8 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+            final boolean allowMalformedNamesAndOptions = schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+            readOID(reader, allowMalformedNamesAndOptions);
 
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
@@ -109,24 +106,24 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readNameDescriptors(reader, schema.allowMalformedNamesAndOptions());
+                    readNameDescriptors(reader, allowMalformedNamesAndOptions);
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    SchemaUtils.readQuotedString(reader);
+                    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.
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("abstract".equalsIgnoreCase(tokenName)) {
                     // This indicates that entries must not include this
                     // objectclass unless they also include a non-abstract
@@ -139,28 +136,23 @@
                     // This indicates that this is an auxiliary objectclass.
                     // We do not need any more parsing for this token.
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    SchemaUtils.readOIDs(reader, schema.allowMalformedNamesAndOptions());
+                    readOIDs(reader, allowMalformedNamesAndOptions);
                 } 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.
-                    SchemaUtils.readExtensions(reader);
+                    readExtensions(reader);
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    final DecodeException e = DecodeException.error(message);
-                    logger.debug(LocalizableMessage.raw("%s", e));
-                    throw e;
+                    throwDecodeException(logger, ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
             return true;
         } catch (final DecodeException de) {
-            invalidReason.append(ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, de
-                    .getMessageObject()));
+            invalidReason.append(ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, de.getMessageObject()));
             return false;
         }
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java
index 0865816..a194590 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierEqualityMatchingRuleImpl.java
@@ -34,6 +34,9 @@
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
 /**
  * This class defines the objectIdentifierMatch matching rule defined in X.520
  * and referenced in RFC 2252. This expects to work on OIDs and will match
@@ -81,7 +84,7 @@
             throws DecodeException {
         final String definition = assertionValue.toString();
         final SubstringReader reader = new SubstringReader(definition);
-        final String oid = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+        final String oid = readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
         final String normalized = resolveNames(schema, oid);
         return DefaultAssertion.equality(ByteString.valueOf(normalized));
     }
@@ -90,7 +93,7 @@
             throws DecodeException {
         final String definition = value.toString();
         final SubstringReader reader = new SubstringReader(definition);
-        final String oid = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+        final String oid = readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
         final String normalized = resolveNames(schema, oid);
         return ByteString.valueOf(normalized);
     }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java
index 40f2e2a..372d9bc 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectIdentifierFirstComponentEqualityMatchingRuleImpl.java
@@ -37,6 +37,9 @@
 
 import com.forgerock.opendj.util.SubstringReader;
 
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
 /**
  * This class implements the objectIdentifierFirstComponentMatch matching rule
  * defined in X.520 and referenced in RFC 2252. This rule is intended for use
@@ -52,11 +55,12 @@
             throws DecodeException {
         final String definition = assertionValue.toString();
         final SubstringReader reader = new SubstringReader(definition);
-        final String oid = SchemaUtils.readOID(reader, schema.allowMalformedNamesAndOptions());
+        final String oid = readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
         final String normalized = ObjectIdentifierEqualityMatchingRuleImpl.resolveNames(schema, oid);
         return DefaultAssertion.equality(ByteString.valueOf(normalized));
     }
 
+    @Override
     public ByteString normalizeAttributeValue(final Schema schema, final ByteSequence value)
             throws DecodeException {
         final String definition = value.toString();
@@ -67,8 +71,7 @@
         reader.skipWhitespaces();
 
         if (reader.remaining() <= 0) {
-            // This means that the value was empty or contained only
-            // whitespace. That is illegal.
+            // Value was empty or contained only whitespace. This is illegal.
             final LocalizableMessage message = ERR_ATTR_SYNTAX_EMPTY_VALUE.get();
             throw DecodeException.error(message);
         }
@@ -85,9 +88,8 @@
         reader.skipWhitespaces();
 
         // The next set of characters must be the OID.
-        final String normalized =
-                ObjectIdentifierEqualityMatchingRuleImpl.resolveNames(schema, SchemaUtils.readOID(
-                        reader, schema.allowMalformedNamesAndOptions()));
+        final String normalized = ObjectIdentifierEqualityMatchingRuleImpl.resolveNames(schema,
+            readOID(reader, schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS)));
         return ByteString.valueOf(normalized);
     }
 }
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
index a19d00f..b8ead17 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/Schema.java
@@ -47,6 +47,7 @@
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.LdapPromise;
 import org.forgerock.opendj.ldap.LinkedAttribute;
+import org.forgerock.opendj.ldap.Option;
 import org.forgerock.opendj.ldap.RDN;
 import org.forgerock.util.Reject;
 import org.forgerock.util.promise.Function;
@@ -54,7 +55,6 @@
 import com.forgerock.opendj.util.StaticUtils;
 
 import static org.forgerock.opendj.ldap.AttributeDescription.*;
-
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 /**
@@ -77,15 +77,7 @@
 
         Schema asStrictSchema();
 
-        boolean allowMalformedNamesAndOptions();
-
-        boolean allowMalformedJPEGPhotos();
-
-        boolean allowMalformedCertificates();
-
-        boolean allowNonStandardTelephoneNumbers();
-
-        boolean allowZeroLengthDirectoryStrings();
+        SchemaOptions getOptions();
 
         MatchingRule getDefaultMatchingRule();
 
@@ -186,28 +178,8 @@
         }
 
         @Override
-        public boolean allowMalformedNamesAndOptions() {
-            return strictImpl.allowMalformedNamesAndOptions();
-        }
-
-        @Override
-        public boolean allowMalformedJPEGPhotos() {
-            return strictImpl.allowMalformedJPEGPhotos();
-        }
-
-        @Override
-        public boolean allowMalformedCertificates() {
-            return strictImpl.allowMalformedCertificates();
-        }
-
-        @Override
-        public boolean allowNonStandardTelephoneNumbers() {
-            return strictImpl.allowNonStandardTelephoneNumbers();
-        }
-
-        @Override
-        public boolean allowZeroLengthDirectoryStrings() {
-            return strictImpl.allowZeroLengthDirectoryStrings();
+        public SchemaOptions getOptions() {
+            return strictImpl.getOptions();
         }
 
         @Override
@@ -439,21 +411,14 @@
         private final Map<String, List<NameForm>> objectClass2NameForms;
         private final List<LocalizableMessage> warnings;
         private final String schemaName;
-        private final boolean allowMalformedJPEGPhotos;
-        private final boolean allowMalformedCertificates;
-        private final boolean allowNonStandardTelephoneNumbers;
-        private final boolean allowZeroLengthDirectoryStrings;
-        private final boolean allowMalformedNamesAndOptions;
+        private final SchemaOptions options;
         private final Syntax defaultSyntax;
         private final MatchingRule defaultMatchingRule;
         private final Schema strictSchema;
         private final Schema nonStrictSchema;
 
-        StrictImpl(final String schemaName, final boolean allowMalformedNamesAndOptions,
-                final boolean allowMalformedJPEGPhotos,
-                final boolean allowMalformedCertificates,
-                final boolean allowNonStandardTelephoneNumbers,
-                final boolean allowZeroLengthDirectoryStrings,
+        StrictImpl(final String schemaName,
+                final SchemaOptions options,
                 final Syntax defaultSyntax,
                 final MatchingRule defaultMatchingRule,
                 final Map<String, Syntax> numericOID2Syntaxes,
@@ -475,17 +440,12 @@
                 final Map<String, List<DITStructureRule>> nameForm2StructureRules,
                 final List<LocalizableMessage> warnings) {
             this.schemaName = schemaName;
-            this.allowMalformedNamesAndOptions = allowMalformedNamesAndOptions;
-            this.allowMalformedJPEGPhotos = allowMalformedJPEGPhotos;
-            this.allowMalformedCertificates = allowMalformedCertificates;
-            this.allowNonStandardTelephoneNumbers = allowNonStandardTelephoneNumbers;
-            this.allowZeroLengthDirectoryStrings = allowZeroLengthDirectoryStrings;
+            this.options = SchemaOptions.unmodifiable(options);
             this.defaultSyntax = defaultSyntax;
             this.defaultMatchingRule = defaultMatchingRule;
             this.numericOID2Syntaxes = Collections.unmodifiableMap(numericOID2Syntaxes);
             this.numericOID2MatchingRules = Collections.unmodifiableMap(numericOID2MatchingRules);
-            this.numericOID2MatchingRuleUses =
-                    Collections.unmodifiableMap(numericOID2MatchingRuleUses);
+            this.numericOID2MatchingRuleUses = Collections.unmodifiableMap(numericOID2MatchingRuleUses);
             this.numericOID2AttributeTypes = Collections.unmodifiableMap(numericOID2AttributeTypes);
             this.numericOID2ObjectClasses = Collections.unmodifiableMap(numericOID2ObjectClasses);
             this.numericOID2NameForms = Collections.unmodifiableMap(numericOID2NameForms);
@@ -516,28 +476,8 @@
         }
 
         @Override
-        public boolean allowMalformedNamesAndOptions() {
-            return allowMalformedNamesAndOptions;
-        }
-
-        @Override
-        public boolean allowMalformedJPEGPhotos() {
-            return allowMalformedJPEGPhotos;
-        }
-
-        @Override
-        public boolean allowMalformedCertificates() {
-            return allowMalformedCertificates;
-        }
-
-        @Override
-        public boolean allowNonStandardTelephoneNumbers() {
-            return allowNonStandardTelephoneNumbers;
-        }
-
-        @Override
-        public boolean allowZeroLengthDirectoryStrings() {
-            return allowZeroLengthDirectoryStrings;
+        public SchemaOptions getOptions() {
+            return options;
         }
 
         @Override
@@ -1116,91 +1056,6 @@
     }
 
     /**
-     * Returns {@code true} if this schema allows certain illegal characters in
-     * OIDs and attribute options. When this compatibility option is set to
-     * {@code true} the following illegal characters will be permitted in
-     * addition to those permitted in section 1.4 of RFC 4512:
-     *
-     * <pre>
-     * USCORE  = %x5F ; underscore ("_")
-     * DOT     = %x2E ; period (".")
-     * </pre>
-     *
-     * By default this compatibility option is set to {@code true} because these
-     * characters are often used for naming purposes (such as collation rules).
-     *
-     * @return {@code true} if this schema allows certain illegal characters in
-     *         OIDs and attribute options.
-     * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight
-     *      Directory Access Protocol (LDAP): Directory Information Models </a>
-     */
-    public boolean allowMalformedNamesAndOptions() {
-        return impl.allowMalformedNamesAndOptions();
-    }
-
-    /**
-     * Returns {@code true} if the JPEG Photo syntax defined for this
-     * schema allows values which do not conform to the JFIF or Exif
-     * specifications.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @return {@code true} if the JPEG Photo syntax defined for this
-     *         schema allows values which do not conform to the JFIF
-     *         of Exit specifications.
-     */
-    public boolean allowMalformedJPEGPhotos() {
-        return impl.allowMalformedJPEGPhotos();
-    }
-
-    /**
-     * Returns {@code true} if the Certificate syntax defined for this
-     * schema allows values which do not conform to the X.509
-     * specifications.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @return {@code true} if the Certificate syntax defined for this
-     *         schema allows values which do not conform to the X.509
-     *         specifications.
-     */
-    public boolean allowMalformedCertificates() {
-        return impl.allowMalformedCertificates();
-    }
-
-    /**
-     * Returns {@code true} if the Telephone Number syntax defined for this
-     * schema allows values which do not conform to the E.123 international
-     * telephone number format.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @return {@code true} if the Telephone Number syntax defined for this
-     *         schema allows values which do not conform to the E.123
-     *         international telephone number format.
-     */
-    public boolean allowNonStandardTelephoneNumbers() {
-        return impl.allowNonStandardTelephoneNumbers();
-    }
-
-    /**
-     * Returns {@code true} if zero-length values will be allowed by the
-     * Directory String syntax defined for this schema. This is technically
-     * forbidden by the LDAP specification, but it was allowed in earlier
-     * versions of the server, and the discussion of the directory string syntax
-     * in RFC 2252 does not explicitly state that they are not allowed.
-     * <p>
-     * By default this compatibility option is set to {@code false}.
-     *
-     * @return {@code true} if zero-length values will be allowed by the
-     *         Directory String syntax defined for this schema, or {@code false}
-     *         if not.
-     */
-    public boolean allowZeroLengthDirectoryStrings() {
-        return impl.allowZeroLengthDirectoryStrings();
-    }
-
-    /**
      * Returns a non-strict view of this schema.
      * <p>
      * See the description of {@link #isStrict()} for more details.
@@ -1224,25 +1079,11 @@
         return impl.asStrictSchema();
     }
 
-    /**
-     * Returns the default matching rule which will be used when parsing
-     * unrecognized attributes.
-     *
-     * @return The default matching rule which will be used when parsing
-     *         unrecognized attributes.
-     */
-    public MatchingRule getDefaultMatchingRule() {
+    MatchingRule getDefaultMatchingRule() {
         return impl.getDefaultMatchingRule();
     }
 
-    /**
-     * Returns the default syntax which will be used when parsing unrecognized
-     * attributes.
-     *
-     * @return The default syntax which will be used when parsing unrecognized
-     *         attributes.
-     */
-    public Syntax getDefaultSyntax() {
+    Syntax getDefaultSyntax() {
         return impl.getDefaultSyntax();
     }
 
@@ -1574,6 +1415,25 @@
     }
 
     /**
+     * Returns the value associated to the provided {@link Option} or the option
+     * default value, if there is no such option in this schema.
+     *
+     * @param <T>
+     *            The option type.
+     * @param option
+     *            The option whose associated value should to be retrieve.
+     * @return The value associated to the provided {@link Option} or the option
+     *         default value, if there is no such option in this schema.
+     */
+    public <T> T getOption(Option<T> option) {
+        return getOptions().get(option);
+    }
+
+    SchemaOptions getOptions() {
+        return impl.getOptions();
+    }
+
+    /**
      * Returns the user-friendly name of this schema which may be used for
      * debugging purposes. The format of the schema name is not defined but
      * should contain the distinguished name of the subschema sub-entry for
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index 064ec94..9ff976c 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -52,6 +52,7 @@
 import org.forgerock.opendj.ldap.Filter;
 import org.forgerock.opendj.ldap.LdapException;
 import org.forgerock.opendj.ldap.LdapPromise;
+import org.forgerock.opendj.ldap.Option;
 import org.forgerock.opendj.ldap.ResultCode;
 import org.forgerock.opendj.ldap.SearchScope;
 import org.forgerock.opendj.ldap.requests.Requests;
@@ -68,8 +69,8 @@
 import static org.forgerock.opendj.ldap.LdapException.*;
 import static org.forgerock.opendj.ldap.schema.Schema.*;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
-
 import static com.forgerock.opendj.ldap.CoreMessages.*;
 import static com.forgerock.opendj.util.StaticUtils.*;
 
@@ -146,14 +147,8 @@
     private Map<String, List<NameForm>> objectClass2NameForms;
     private String schemaName;
     private List<LocalizableMessage> warnings;
-    private boolean allowNonStandardTelephoneNumbers;
-    private boolean allowZeroLengthDirectoryStrings;
-    private boolean allowMalformedNamesAndOptions;
-    private boolean allowMalformedJPEGPhotos;
-    private boolean allowMalformedCertificates;
+    private SchemaOptions options;
 
-    private String defaultSyntaxOID;
-    private String defaultMatchingRuleOID;
 
     /** A schema which should be copied into this builder on any mutation. */
     private Schema copyOnWriteSchema;
@@ -213,6 +208,10 @@
         preLazyInitBuilder(schemaName, null);
     }
 
+    private Boolean allowsMalformedNamesAndOptions() {
+        return options.get(ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+    }
+
     /**
      * Adds the provided attribute type definition to this schema builder.
      *
@@ -246,9 +245,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the definition was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRTYPE_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_ATTRTYPE_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -265,7 +262,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String oid = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+            final String oid = readOID(reader, allowsMalformedNamesAndOptions());
 
             List<String> names = Collections.emptyList();
             String description = "".intern();
@@ -291,18 +288,18 @@
             // the end of the definition. But before we start, set default
             // values for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
+                    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 = SchemaUtils.readQuotedString(reader);
+                    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
@@ -312,22 +309,19 @@
                     // This specifies the name or OID of the superior attribute
                     // type from which this attribute type should inherit its
                     // properties.
-                    superiorType = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    superiorType = readOID(reader, allowsMalformedNamesAndOptions());
                 } else if ("equality".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the equality matching
                     // rule to use for this attribute type.
-                    equalityMatchingRule =
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    equalityMatchingRule = readOID(reader, allowsMalformedNamesAndOptions());
                 } else if ("ordering".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the ordering matching
                     // rule to use for this attribute type.
-                    orderingMatchingRule =
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    orderingMatchingRule = readOID(reader, allowsMalformedNamesAndOptions());
                 } else if ("substr".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the substring matching
                     // rule to use for this attribute type.
-                    substringMatchingRule =
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    substringMatchingRule = readOID(reader, allowsMalformedNamesAndOptions());
                 } else if ("syntax".equalsIgnoreCase(tokenName)) {
                     // This specifies the numeric OID of the syntax for this
                     // matching rule. It may optionally be immediately followed
@@ -337,7 +331,7 @@
                     // implementation will ignore any such length because it
                     // does not impose any practical limit on the length of attribute
                     // values.
-                    syntax = SchemaUtils.readOIDLen(reader, allowMalformedNamesAndOptions);
+                    syntax = readOIDLen(reader, allowsMalformedNamesAndOptions());
                 } else if ("single-definition".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are allowed
                     // to have at most one definition. We do not need any more
@@ -384,9 +378,8 @@
                     } else if ("dsaoperation".equalsIgnoreCase(usageStr)) {
                         attributeUsage = AttributeUsage.DSA_OPERATION;
                     } else {
-                        final LocalizableMessage message =
-                            WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition, usageStr);
-                        throw new LocalizedIllegalArgumentException(message);
+                        throw new LocalizedIllegalArgumentException(
+                            WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition, usageStr));
                     }
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
@@ -399,9 +392,8 @@
                     }
                     extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
@@ -415,9 +407,8 @@
             }
 
             if (superiorType == null && syntax == null) {
-                final LocalizableMessage msg =
-                        WARN_ATTR_SYNTAX_ATTRTYPE_MISSING_SYNTAX_AND_SUPERIOR.get(definition);
-                throw new LocalizedIllegalArgumentException(msg);
+                throw new LocalizedIllegalArgumentException(
+                    WARN_ATTR_SYNTAX_ATTRTYPE_MISSING_SYNTAX_AND_SUPERIOR.get(definition));
             }
 
             final AttributeType attrType =
@@ -545,8 +536,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DCR_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_DCR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -563,8 +553,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String structuralClass =
-                    SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+            final String structuralClass = readOID(reader, allowsMalformedNamesAndOptions());
 
             List<String> names = Collections.emptyList();
             String description = "".intern();
@@ -584,34 +573,31 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
+                    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 = SchemaUtils.readQuotedString(reader);
+                    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;
                 } else if ("aux".equalsIgnoreCase(tokenName)) {
-                    auxiliaryClasses = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    auxiliaryClasses = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    requiredAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    requiredAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    optionalAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    optionalAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("not".equalsIgnoreCase(tokenName)) {
-                    prohibitedAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    prohibitedAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } 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
@@ -621,11 +607,10 @@
                     if (extraProperties.isEmpty()) {
                         extraProperties = new HashMap<String, List<String>>();
                     }
-                    extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
+                    extraProperties.put(tokenName, readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_DCR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_DCR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
@@ -774,8 +759,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_DSR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -792,7 +776,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final Integer ruleID = SchemaUtils.readRuleID(reader);
+            final Integer ruleID = readRuleID(reader);
 
             List<String> names = Collections.emptyList();
             String description = "".intern();
@@ -810,27 +794,27 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
+                    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 = SchemaUtils.readQuotedString(reader);
+                    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;
                 } else if ("form".equalsIgnoreCase(tokenName)) {
-                    nameForm = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    nameForm = readOID(reader, allowsMalformedNamesAndOptions());
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
-                    superiorRules = SchemaUtils.readRuleIDs(reader);
+                    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
@@ -840,17 +824,15 @@
                     if (extraProperties.isEmpty()) {
                         extraProperties = new HashMap<String, List<String>>();
                     }
-                    extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
+                    extraProperties.put(tokenName, readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_DSR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_DSR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             if (nameForm == null) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_DSR_NO_NAME_FORM.get(definition));
             }
 
             if (!extraProperties.isEmpty()) {
@@ -862,8 +844,7 @@
                             superiorRules, extraProperties, definition);
             addDITStructureRule(rule, overwrite);
         } catch (final DecodeException e) {
-            final LocalizableMessage msg =
-                    ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, e.getMessageObject());
+            final LocalizableMessage msg = ERR_ATTR_SYNTAX_DSR_INVALID1.get(definition, e.getMessageObject());
             throw new LocalizedIllegalArgumentException(msg, e.getCause());
         }
         return this;
@@ -946,8 +927,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MR_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MR_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -964,10 +944,9 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final MatchingRule.Builder matchingRuleBuilder =
-                    new MatchingRule.Builder(
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions), this)
-                            .definition(definition);
+            final MatchingRule.Builder matchingRuleBuilder = new MatchingRule.Builder(
+                readOID(reader, allowsMalformedNamesAndOptions()), this);
+            matchingRuleBuilder.definition(definition);
 
             String syntax = null;
             // At this point, we should have a pretty specific syntax that
@@ -979,25 +958,25 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    matchingRuleBuilder.names(SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions));
+                    matchingRuleBuilder.names(readNameDescriptors(reader, allowsMalformedNamesAndOptions()));
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the matching rule. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    matchingRuleBuilder.description(SchemaUtils.readQuotedString(reader));
+                    matchingRuleBuilder.description(readQuotedString(reader));
                 } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the matching rule should be
                     // considered obsolete. We do not need to do any more
                     // parsing for this token.
                     matchingRuleBuilder.obsolete(true);
                 } else if ("syntax".equalsIgnoreCase(tokenName)) {
-                    syntax = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    syntax = readOID(reader, allowsMalformedNamesAndOptions());
                     matchingRuleBuilder.syntaxOID(syntax);
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
@@ -1005,20 +984,17 @@
                     // or an open parenthesis followed by one or more values in
                     // single quotes separated by spaces followed by a close
                     // parenthesis.
-                    final List<String> extensions = SchemaUtils.readExtensions(reader);
-                    matchingRuleBuilder.extraProperties(tokenName, extensions
-                            .toArray(new String[extensions.size()]));
+                    final List<String> extensions = readExtensions(reader);
+                    matchingRuleBuilder.extraProperties(tokenName, extensions.toArray(new String[extensions.size()]));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_MR_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_MR_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that a syntax was specified.
             if (syntax == null) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MR_NO_SYNTAX.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MR_NO_SYNTAX.get(definition));
             }
             if (overwrite) {
                 matchingRuleBuilder.addToSchemaOverwrite();
@@ -1066,9 +1042,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_MRUSE_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MRUSE_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -1085,7 +1059,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String oid = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+            final String oid = readOID(reader, allowsMalformedNamesAndOptions());
 
             List<String> names = Collections.emptyList();
             String description = "".intern();
@@ -1102,25 +1076,25 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
+                    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 = SchemaUtils.readQuotedString(reader);
+                    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;
                 } else if ("applies".equalsIgnoreCase(tokenName)) {
-                    attributes = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    attributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } 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
@@ -1130,18 +1104,16 @@
                     if (extraProperties.isEmpty()) {
                         extraProperties = new HashMap<String, List<String>>();
                     }
-                    extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
+                    extraProperties.put(tokenName, readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_MRUSE_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_MRUSE_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that the set of attributes was defined.
             if (attributes == null || attributes.size() == 0) {
-                final LocalizableMessage message = ERR_ATTR_SYNTAX_MRUSE_NO_ATTR.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_MRUSE_NO_ATTR.get(definition));
             }
 
             if (!extraProperties.isEmpty()) {
@@ -1248,9 +1220,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_NAME_FORM_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -1267,10 +1237,9 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final NameForm.Builder nameFormBuilder =
-                    new NameForm.Builder(
-                            SchemaUtils.readOID(reader, allowMalformedNamesAndOptions), this)
-                            .definition(definition);
+            final NameForm.Builder nameFormBuilder = new NameForm.Builder(
+                readOID(reader, allowsMalformedNamesAndOptions()), this);
+            nameFormBuilder.definition(definition);
 
             // Required properties :
             String structuralOID = null;
@@ -1285,62 +1254,54 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    nameFormBuilder.names(SchemaUtils.readNameDescriptors(reader,
-                            allowMalformedNamesAndOptions));
+                    nameFormBuilder.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.
-                    nameFormBuilder.description(SchemaUtils.readQuotedString(reader));
+                    nameFormBuilder.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.
                     nameFormBuilder.obsolete(true);
                 } else if ("oc".equalsIgnoreCase(tokenName)) {
-                    structuralOID = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+                    structuralOID = readOID(reader, allowsMalformedNamesAndOptions());
                     nameFormBuilder.structuralObjectClassOID(structuralOID);
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    requiredAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    requiredAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                     nameFormBuilder.requiredAttributes(requiredAttributes);
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    nameFormBuilder.optionalAttributes(SchemaUtils.readOIDs(reader,
-                            allowMalformedNamesAndOptions));
+                    nameFormBuilder.optionalAttributes(readOIDs(reader, allowsMalformedNamesAndOptions()));
                 } 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.
-                    final List<String> extensions = SchemaUtils.readExtensions(reader);
-                    nameFormBuilder.extraProperties(tokenName, extensions
-                            .toArray(new String[extensions.size()]));
+                    final List<String> extensions = readExtensions(reader);
+                    nameFormBuilder.extraProperties(tokenName, extensions.toArray(new String[extensions.size()]));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_NAME_FORM_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
             // Make sure that a structural class was specified. If not, then
             // it cannot be valid and the name form cannot be build.
             if (structuralOID == null) {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_NO_STRUCTURAL_CLASS1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(
+                    ERR_ATTR_SYNTAX_NAME_FORM_NO_STRUCTURAL_CLASS1.get(definition));
             }
 
             if (requiredAttributes.isEmpty()) {
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_NAME_FORM_NO_REQUIRED_ATTR.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_NAME_FORM_NO_REQUIRED_ATTR.get(definition));
             }
 
             if (overwrite) {
@@ -1393,74 +1354,6 @@
     }
 
     /**
-     * Sets the default syntax which will be used when parsing unrecognized
-     * attributes.
-     * <p>
-     * By default the {@link CoreSchema#getOctetStringSyntax() OctetString}
-     * syntax will be used.
-     *
-     * @param syntax
-     *            The default syntax which will be used when parsing
-     *            unrecognized attributes.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder defaultSyntax(final Syntax syntax) {
-        return defaultSyntax(syntax.getOID());
-    }
-
-    /**
-     * Sets the default matching rule which will be used when parsing
-     * unrecognized attributes.
-     * <p>
-     * By default the {@link CoreSchema#getOctetStringMatchingRule()
-     * OctetString} matching rule will be used.
-     *
-     * @param rule
-     *            The default matching rule which will be used when parsing
-     *            unrecognized attributes.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder defaultMatchingRule(final MatchingRule rule) {
-        return defaultMatchingRule(rule.getOID());
-    }
-
-    /**
-     * Sets the default syntax which will be used when parsing unrecognized
-     * attributes.
-     * <p>
-     * By default the {@link CoreSchema#getOctetStringSyntax() OctetString}
-     * syntax will be used.
-     *
-     * @param syntaxOID
-     *            The default syntax which will be used when parsing
-     *            unrecognized attributes.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder defaultSyntax(final String syntaxOID) {
-        lazyInitBuilder();
-        this.defaultSyntaxOID = syntaxOID;
-        return this;
-    }
-
-    /**
-     * Sets the default matching rule which will be used when parsing
-     * unrecognized attributes.
-     * <p>
-     * By default the {@link CoreSchema#getOctetStringMatchingRule()
-     * OctetString} matching rule will be used.
-     *
-     * @param ruleOID
-     *            The default matching rule which will be used when parsing
-     *            unrecognized attributes.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder defaultMatchingRule(final String ruleOID) {
-        lazyInitBuilder();
-        this.defaultMatchingRuleOID = ruleOID;
-        return this;
-    }
-
-    /**
      * Duplicates the matching rule.
      *
      * @param matchingRule
@@ -1561,9 +1454,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_OBJECTCLASS_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_OBJECTCLASS_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -1580,7 +1471,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String oid = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+            final String oid = readOID(reader, allowsMalformedNamesAndOptions());
 
             List<String> names = Collections.emptyList();
             String description = "".intern();
@@ -1600,25 +1491,25 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = SchemaUtils.readNameDescriptors(reader, allowMalformedNamesAndOptions);
+                    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 = SchemaUtils.readQuotedString(reader);
+                    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;
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
-                    superiorClasses = SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    superiorClasses = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("abstract".equalsIgnoreCase(tokenName)) {
                     // This indicates that entries must not include this
                     // objectclass unless they also include a non-abstract
@@ -1634,11 +1525,9 @@
                     // do not need any more parsing for this token.
                     objectClassType = ObjectClassType.AUXILIARY;
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    requiredAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    requiredAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    optionalAttributes =
-                            SchemaUtils.readOIDs(reader, allowMalformedNamesAndOptions);
+                    optionalAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } 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
@@ -1648,11 +1537,10 @@
                     if (extraProperties.isEmpty()) {
                         extraProperties = new HashMap<String, List<String>>();
                     }
-                    extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
+                    extraProperties.put(tokenName, readExtensions(reader));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
@@ -2124,9 +2012,7 @@
             if (reader.remaining() <= 0) {
                 // This means that the value was empty or contained only
                 // whitespace. That is illegal.
-                final LocalizableMessage message =
-                        ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE1.get(definition);
-                throw new LocalizedIllegalArgumentException(message);
+                throw new LocalizedIllegalArgumentException(ERR_ATTR_SYNTAX_ATTRSYNTAX_EMPTY_VALUE1.get(definition));
             }
 
             // The next character must be an open parenthesis. If it is not,
@@ -2143,7 +2029,7 @@
             reader.skipWhitespaces();
 
             // The next set of characters must be the OID.
-            final String oid = SchemaUtils.readOID(reader, allowMalformedNamesAndOptions);
+            final String oid = readOID(reader, allowsMalformedNamesAndOptions());
             final Syntax.Builder syntaxBuilder = new Syntax.Builder(oid, this).definition(definition);
 
             // At this point, we should have a pretty specific syntax that
@@ -2155,7 +2041,7 @@
             // the end of the value. But before we start, set default values
             // for everything else we might need to know.
             while (true) {
-                final String tokenName = SchemaUtils.readTokenName(reader);
+                final String tokenName = readTokenName(reader);
 
                 if (tokenName == null) {
                     // No more tokens.
@@ -2163,19 +2049,18 @@
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the syntax. It is an
                     // arbitrary string of characters enclosed in single quotes.
-                    syntaxBuilder.description(SchemaUtils.readQuotedString(reader));
+                    syntaxBuilder.description(readQuotedString(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.
-                    final List<String> extensions = SchemaUtils.readExtensions(reader);
+                    final List<String> extensions = readExtensions(reader);
                     syntaxBuilder.extraProperties(tokenName, extensions.toArray(new String[extensions.size()]));
                 } else {
-                    final LocalizableMessage message =
-                            ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_TOKEN1.get(definition, tokenName);
-                    throw new LocalizedIllegalArgumentException(message);
+                    throw new LocalizedIllegalArgumentException(
+                        ERR_ATTR_SYNTAX_ATTRSYNTAX_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
@@ -2184,7 +2069,6 @@
                 if ("x-enum".equalsIgnoreCase(property.getKey())) {
                     final EnumSyntaxImpl enumImpl = new EnumSyntaxImpl(oid, property.getValue());
                     syntaxBuilder.implementation(enumImpl);
-
                     syntaxBuilder.addToSchema(overwrite);
 
                     buildMatchingRule(enumImpl.getOrderingMatchingRule())
@@ -2206,110 +2090,10 @@
         return this;
     }
 
-    /**
-     * Specifies whether or not the schema should allow certain illegal
-     * characters in OIDs and attribute options. When this compatibility option
-     * is set to {@code true} the following illegal characters will be permitted
-     * in addition to those permitted in section 1.4 of RFC 4512:
-     *
-     * <pre>
-     * USCORE  = %x5F ; underscore ("_")
-     * DOT     = %x2E ; period (".")
-     * </pre>
-     *
-     * By default this compatibility option is set to {@code true} because these
-     * characters are often used for naming purposes (such as collation rules).
-     *
-     * @param allowMalformedNamesAndOptions
-     *            {@code true} if the schema should allow certain illegal
-     *            characters in OIDs and attribute options.
-     * @return A reference to this {@code SchemaBuilder}.
-     * @see <a href="http://tools.ietf.org/html/rfc4512">RFC 4512 - Lightweight
-     *      Directory Access Protocol (LDAP): Directory Information Models </a>
-     */
-    public SchemaBuilder allowMalformedNamesAndOptions(final boolean allowMalformedNamesAndOptions) {
+    SchemaOptions getOptions() {
         lazyInitBuilder();
 
-        this.allowMalformedNamesAndOptions = allowMalformedNamesAndOptions;
-        return this;
-    }
-
-    /**
-     * Specifies whether or not the JPEG Photo syntax should allow values which
-     * do not conform to the JFIF or Exif specifications.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @param allowMalformedJPEGPhotos
-     *            {@code true} if the JPEG Photo syntax should allow values
-     *            which do not conform to the JFIF or Exif specifications.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder allowMalformedJPEGPhotos(final boolean allowMalformedJPEGPhotos) {
-        lazyInitBuilder();
-
-        this.allowMalformedJPEGPhotos = allowMalformedJPEGPhotos;
-        return this;
-    }
-
-    /**
-     * Specifies whether or not the Certificate syntax should allow values which
-     * do not conform to the X.509 specifications.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @param allowMalformedCertificates
-     *            {@code true} if the Certificate syntax should allow values
-     *            which do not conform to the X.509 specifications.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder allowMalformedCertificates(final boolean allowMalformedCertificates) {
-        lazyInitBuilder();
-
-        this.allowMalformedCertificates = allowMalformedCertificates;
-        return this;
-    }
-
-    /**
-     * Specifies whether or not the Telephone Number syntax should allow values
-     * which do not conform to the E.123 international telephone number format.
-     * <p>
-     * By default this compatibility option is set to {@code true}.
-     *
-     * @param allowNonStandardTelephoneNumbers
-     *            {@code true} if the Telephone Number syntax should allow
-     *            values which do not conform to the E.123 international
-     *            telephone number format.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder allowNonStandardTelephoneNumbers(
-            final boolean allowNonStandardTelephoneNumbers) {
-        lazyInitBuilder();
-
-        this.allowNonStandardTelephoneNumbers = allowNonStandardTelephoneNumbers;
-        return this;
-    }
-
-    /**
-     * Specifies whether or not zero-length values will be allowed by the
-     * Directory String syntax. This is technically forbidden by the LDAP
-     * specification, but it was allowed in earlier versions of the server, and
-     * the discussion of the directory string syntax in RFC 2252 does not
-     * explicitly state that they are not allowed.
-     * <p>
-     * By default this compatibility option is set to {@code false}.
-     *
-     * @param allowZeroLengthDirectoryStrings
-     *            {@code true} if zero-length values will be allowed by the
-     *            Directory String syntax, or {@code false} if not.
-     * @return A reference to this {@code SchemaBuilder}.
-     */
-    public SchemaBuilder allowZeroLengthDirectoryStrings(
-            final boolean allowZeroLengthDirectoryStrings) {
-        lazyInitBuilder();
-
-        this.allowZeroLengthDirectoryStrings = allowZeroLengthDirectoryStrings;
-        return this;
+        return options;
     }
 
     /**
@@ -2499,6 +2283,24 @@
     }
 
     /**
+     * Sets a schema option overriding any previous values for the option.
+     *
+     * @param <T>
+     *            The option type.
+     * @param option
+     *            Option with which the specified value is to be associated.
+     * @param value
+     *            Value to be associated with the specified option.
+     * @return A reference to this schema builder.
+     * @throws UnsupportedOperationException
+     *             If the schema builder options are read only.
+     */
+    public <T> SchemaBuilder setOption(final Option<T> option, T value) {
+        getOptions().set(option, value);
+        return this;
+    }
+
+    /**
      * Returns a strict {@code Schema} containing all of the schema elements
      * contained in this schema builder as well as the same set of schema
      * compatibility options.
@@ -2528,20 +2330,18 @@
             localSchemaName = String.format("Schema#%d", NEXT_SCHEMA_ID.getAndIncrement());
         }
 
-        Syntax defaultSyntax = numericOID2Syntaxes.get(defaultSyntaxOID);
+        Syntax defaultSyntax = numericOID2Syntaxes.get(options.get(DEFAULT_SYNTAX_OID));
         if (defaultSyntax == null) {
             defaultSyntax = Schema.getCoreSchema().getDefaultSyntax();
         }
 
-        MatchingRule defaultMatchingRule = numericOID2MatchingRules.get(defaultMatchingRuleOID);
+        MatchingRule defaultMatchingRule =  numericOID2MatchingRules.get(options.get(DEFAULT_MATCHING_RULE_OID));
         if (defaultMatchingRule == null) {
             defaultMatchingRule = Schema.getCoreSchema().getDefaultMatchingRule();
         }
 
         final Schema schema =
-                new Schema.StrictImpl(localSchemaName, allowMalformedNamesAndOptions,
-                        allowMalformedJPEGPhotos, allowMalformedCertificates,
-                        allowNonStandardTelephoneNumbers, allowZeroLengthDirectoryStrings,
+                new Schema.StrictImpl(localSchemaName, options,
                         defaultSyntax, defaultMatchingRule, numericOID2Syntaxes,
                         numericOID2MatchingRules, numericOID2MatchingRuleUses,
                         numericOID2AttributeTypes, numericOID2ObjectClasses, numericOID2NameForms,
@@ -2829,13 +2629,7 @@
     private void lazyInitBuilder() {
         // Lazy initialization.
         if (numericOID2Syntaxes == null) {
-            allowMalformedNamesAndOptions = true;
-            allowMalformedJPEGPhotos = true;
-            allowMalformedCertificates = true;
-            allowNonStandardTelephoneNumbers = true;
-            allowZeroLengthDirectoryStrings = false;
-            defaultSyntaxOID = SchemaConstants.SYNTAX_OCTET_STRING_OID;
-            defaultMatchingRuleOID = SchemaConstants.EMR_OCTET_STRING_OID;
+            options = defaultSchemaOptions();
 
             numericOID2Syntaxes = new LinkedHashMap<String, Syntax>();
             numericOID2MatchingRules = new LinkedHashMap<String, MatchingRule>();
@@ -2862,15 +2656,7 @@
         if (copyOnWriteSchema != null) {
             // Copy the schema.
             addSchema0(copyOnWriteSchema, true);
-
-            allowMalformedNamesAndOptions = copyOnWriteSchema.allowMalformedNamesAndOptions();
-            allowMalformedJPEGPhotos = copyOnWriteSchema.allowMalformedJPEGPhotos();
-            allowMalformedCertificates = copyOnWriteSchema.allowMalformedCertificates();
-            allowNonStandardTelephoneNumbers = copyOnWriteSchema.allowNonStandardTelephoneNumbers();
-            allowZeroLengthDirectoryStrings = copyOnWriteSchema.allowZeroLengthDirectoryStrings();
-            defaultSyntaxOID = copyOnWriteSchema.getDefaultSyntax().getOID();
-            defaultMatchingRuleOID = copyOnWriteSchema.getDefaultMatchingRule().getOID();
-
+            options = SchemaOptions.copyOf(copyOnWriteSchema.getOptions());
             copyOnWriteSchema = null;
         }
     }
@@ -2879,13 +2665,7 @@
         this.schemaName = schemaName;
         this.copyOnWriteSchema = copyOnWriteSchema;
 
-        this.allowMalformedNamesAndOptions = true;
-        this.allowMalformedJPEGPhotos = true;
-        this.allowMalformedCertificates = true;
-        this.allowNonStandardTelephoneNumbers = true;
-        this.allowZeroLengthDirectoryStrings = false;
-        this.defaultSyntaxOID = null;
-        this.defaultMatchingRuleOID = null;
+        this.options = null;
 
         this.numericOID2Syntaxes = null;
         this.numericOID2MatchingRules = null;
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaOptions.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaOptions.java
new file mode 100644
index 0000000..ba30b98
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaOptions.java
@@ -0,0 +1,214 @@
+/*
+ * 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 2014 ForgeRock AS
+ */
+package org.forgerock.opendj.ldap.schema;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
+
+import org.forgerock.opendj.ldap.Option;
+
+import static java.util.Collections.*;
+
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+
+/**
+ * Common options for LDAP schemas.
+ * <p>
+ * For example you set schema option as you want when using a schema.
+ *
+ * <pre>
+ * // Retrieves options from builder.
+ * SchemaOptions options = new SchemaBuilder().getOptions();
+ * // Creates a new option.
+ * Option myIntegerOption = options.set(Option.of(Integer.class, 0));
+ * // Retrieves option value from SchemaOption
+ * boolean allowMalformedNamesAndOptions = options.get(SchemaOptions.ALLOW_MALFORMED_NAMES_AND_OPTIONS);
+ * </pre>
+ */
+public final class SchemaOptions {
+    /**
+     * Specifies whether the schema should allow certain illegal
+     * characters in OIDs and attribute options. When this compatibility option
+     * is set to {@code true} the following illegal characters will be permitted
+     * in addition to those permitted in section 1.4 of RFC 4512:
+     *
+     * <pre>
+     * USCORE  = %x5F ; underscore ("_")
+     * DOT     = %x2E ; period (".")
+     * </pre>
+     *
+     * By default this compatibility option is set to {@code true} because these
+     * characters are often used for naming purposes (such as collation rules).
+     */
+    public static final Option<Boolean> ALLOW_MALFORMED_NAMES_AND_OPTIONS = Option.withDefault(true);
+
+    /**
+     * Specifies whether the JPEG Photo syntax should allow values which
+     * do not conform to the JFIF or Exif specifications.
+     * <p>
+     * By default this compatibility option is set to {@code true}.
+     */
+    public static final Option<Boolean> ALLOW_MALFORMED_JPEG_PHOTOS = Option.withDefault(true);
+
+    /**
+     * Specifies whether the Certificate syntax should allow values which
+     * do not conform to the X.509 specifications.
+     * <p>
+     * By default this compatibility option is set to {@code true}.
+     */
+    public static final Option<Boolean> ALLOW_MALFORMED_CERTIFICATES = Option.withDefault(true);
+
+    /**
+     * Specifies whether the Telephone Number syntax should allow values
+     * which do not conform to the E.123 international telephone number format.
+     * <p>
+     * By default this compatibility option is set to {@code true}.
+     */
+    public static final Option<Boolean> ALLOW_NON_STANDARD_TELEPHONE_NUMBERS = Option.withDefault(true);
+
+    /**
+     * Specifies whether zero-length values will be allowed by the
+     * Directory String syntax. This is technically forbidden by the LDAP
+     * specification, but it was allowed in earlier versions of the server, and
+     * the discussion of the directory string syntax in RFC 2252 does not
+     * explicitly state that they are not allowed.
+     * <p>
+     * By default this compatibility option is set to {@code false}.
+     */
+    public static final Option<Boolean> ALLOW_ZERO_LENGTH_DIRECTORY_STRINGS = Option.withDefault(false);
+
+    /**
+     * Specifies the OID of the default syntax which will be used when parsing
+     * unrecognized attributes.
+     * <p>
+     * By default the {@link SchemaConstants#SYNTAX_OCTET_STRING_OID OctetString}
+     * syntax will be used.
+     */
+    public static final Option<String> DEFAULT_SYNTAX_OID = Option.of(String.class, SYNTAX_OCTET_STRING_OID);
+
+    /**
+     * Specifies the OID of the default matching rule which will be used when
+     * parsing unrecognized attributes.
+     * <p>
+     * By default the {@link SchemaConstants#EMR_OCTET_STRING_OID OctetString}
+     * matching rule will be used.
+     */
+    public static final Option<String> DEFAULT_MATCHING_RULE_OID = Option.of(String.class, EMR_OCTET_STRING_OID);
+
+    /**
+     * Indicates whether country code values are required to strictly
+     * comply with the standard definition for this syntax.
+     * <p>
+     * When set to false, country codes will not be validated and, as a result
+     * any string containing 2 characters will be acceptable.
+     * By default this compatibility option is set to {@code true}.
+     */
+    public static final Option<Boolean> STRICT_FORMAT_FOR_COUNTRY_STRINGS = Option.withDefault(true);
+
+    /**
+     * Indicates whether the minimum upper bound value should be stripped from
+     * the Attribute Type Syntax Description.
+     * <p>
+     * By default this compatibility option is set to {@code false}.
+     */
+    public static final Option<Boolean> STRIP_UPPER_BOUND_FOR_ATTRIBUTE_TYPE = Option.withDefault(false);
+
+    private final Map<Option<?>, Object> options;
+
+    /**
+     * Creates a new set of schema options with default settings.
+     *
+     * @return A new {@link SchemaOptions} with default settings.
+     */
+    static SchemaOptions defaultSchemaOptions() {
+        return new SchemaOptions(new IdentityHashMap<Option<?>, Object>());
+    }
+
+    /**
+     * Creates a new schema options object by copying the provided schema
+     * options. The options names and values will all be copied.
+     *
+     * @param schemaOptions
+     *            The schema options to be copied.
+     * @return A new schema options object created by copying the provided schema
+     *         options.
+     */
+    static SchemaOptions copyOf(SchemaOptions schemaOptions) {
+        return new SchemaOptions(new IdentityHashMap<Option<?>, Object>(schemaOptions.options));
+    }
+
+    /**
+     * Returns an unmodifiable {@link SchemaOptions} copy of this set of options.
+     *
+     * @param schemaOptions
+     *            The schema options to be copied.
+     * @return An unmodifiable {@link SchemaOptions} view of this set of options.
+     */
+    static SchemaOptions unmodifiable(SchemaOptions schemaOptions) {
+        return new SchemaOptions(unmodifiableMap(new IdentityHashMap<Option<?>, Object>(schemaOptions.options)));
+    }
+
+    private SchemaOptions(Map<Option<?>, Object> optionsMap) {
+        this.options = optionsMap;
+    }
+
+    /**
+     * Returns the value to which the specified {@link Option} is mapped, or
+     * {@code null} if this options set contains no mapping for the {@link Option}.
+     *
+     * @param <T>
+     *            The option type.
+     * @param option
+     *            The option whose associated value is to be returned.
+     * @return The value to which the specified option is mapped, or null if
+     *         this options set contains no mapping for the option.
+     */
+    <T> T get(Option<T> option) {
+        return option.getValue(options.get(option));
+    }
+
+    /**
+     * Associates the specified option value with the specified option in this
+     * set of options. (optional operation). If this set of options previously
+     * contained a mapping for the option, the old value is replaced by the
+     * specified value.
+     *
+     * @param <T>
+     *            The option type.
+     * @param option
+     *            Option with which the specified value is to be associated.
+     * @param value
+     *            Value to be associated with the specified option.
+     * @return A reference to this set of options.
+     * @throws UnsupportedOperationException
+     *         If this set of options is read only.
+     */
+    <T> SchemaOptions set(Option<T> option, T value) {
+        options.put(option, value);
+        return this;
+    }
+
+}
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java
index 59652bd..baa3756 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaUtils.java
@@ -39,6 +39,8 @@
 import java.util.Map;
 import java.util.Set;
 
+import org.forgerock.i18n.LocalizableMessage;
+import org.forgerock.i18n.slf4j.LocalizedLogger;
 import org.forgerock.opendj.ldap.ByteSequence;
 import org.forgerock.opendj.ldap.ByteString;
 import org.forgerock.opendj.ldap.DecodeException;
@@ -743,6 +745,12 @@
         return ByteString.valueOf(buffer.toString());
     }
 
+    static void throwDecodeException(LocalizedLogger logger, LocalizableMessage message) throws DecodeException {
+        final DecodeException e = DecodeException.error(message);
+        logger.debug(LocalizableMessage.raw("%s", e));
+        throw e;
+    }
+
     private static void throwIfIA5IllegalCharacter(StringBuilder buffer, ByteSequence value) throws DecodeException {
         // Replace any consecutive spaces with a single space and watch out
         // for non-ASCII characters.
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java
index 5ee8eb5..ca2424d 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/TelephoneNumberSyntaxImpl.java
@@ -35,6 +35,7 @@
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.EMR_TELEPHONE_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SMR_TELEPHONE_OID;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_TELEPHONE_NAME;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 
 import org.forgerock.i18n.LocalizableMessage;
 import org.forgerock.i18n.LocalizableMessageBuilder;
@@ -93,7 +94,7 @@
 
         final int length = valueStr.length();
 
-        if (!schema.allowNonStandardTelephoneNumbers()) {
+        if (!schema.getOption(ALLOW_NON_STANDARD_TELEPHONE_NUMBERS)) {
             // If the value does not start with a plus sign, then that's not
             // acceptable.
             if (valueStr.charAt(0) != '+') {
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxTest.java
index 6ecf3ef..eb65273 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxTest.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/CertificateSyntaxTest.java
@@ -32,7 +32,9 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.forgerock.opendj.ldap.schema.Schema.*;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.SYNTAX_CERTIFICATE_OID;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 import static org.testng.Assert.*;
 
 /**
@@ -138,7 +140,7 @@
     @Test(dataProvider = "acceptableValues")
     public void testAllowMalformedCertificates(ByteString value, Boolean result) {
         // Make sure that the specified class can be instantiated as a task.
-        SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema()).allowMalformedCertificates(true);
+        SchemaBuilder builder = new SchemaBuilder(getCoreSchema()).setOption(ALLOW_MALFORMED_CERTIFICATES, true);
         final Syntax syntax = builder.toSchema().getSyntax(SYNTAX_CERTIFICATE_OID);
 
         final LocalizableMessageBuilder reason = new LocalizableMessageBuilder();
@@ -155,7 +157,8 @@
      * @return An instance of the attribute syntax that must be tested.
      */
     protected Syntax getRule() {
-        SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema()).allowMalformedCertificates(false);
+        SchemaBuilder builder = new SchemaBuilder(getCoreSchema()).setOption(ALLOW_MALFORMED_CERTIFICATES, false);
+
         return builder.toSchema().getSyntax(SYNTAX_CERTIFICATE_OID);
     }
 }
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
index a5eb87b..4982cdc 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
@@ -45,7 +45,9 @@
 
 import static org.fest.assertions.Assertions.*;
 import static org.fest.assertions.Fail.*;
+import static org.forgerock.opendj.ldap.schema.CoreSchema.*;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
 import static org.forgerock.opendj.ldap.spi.LdapPromises.*;
 import static org.mockito.Matchers.*;
 import static org.mockito.Mockito.*;
@@ -197,9 +199,8 @@
         assertThat(schema.getAttributeTypes().containsAll(baseSchema.getAttributeTypes()));
         assertThat(schema.getAttributeType("testtype")).isNotNull();
         assertThat(schema.getSchemaName()).isEqualTo(baseSchema.getSchemaName());
-        assertThat(schema.allowMalformedNamesAndOptions()).isEqualTo(
-                baseSchema.allowMalformedNamesAndOptions());
-
+        assertThat(schema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS))
+                .isEqualTo(baseSchema.getOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS));
     }
 
     /**
@@ -2018,11 +2019,11 @@
     @Test
     public void testOverrideDefaultSyntax() {
         final Schema schema =
-                new SchemaBuilder(Schema.getCoreSchema()).defaultSyntax(
-                        CoreSchema.getDirectoryStringSyntax()).toSchema().asNonStrictSchema();
-        assertThat(schema.getDefaultSyntax()).isEqualTo(CoreSchema.getDirectoryStringSyntax());
-        assertThat(schema.getAttributeType("dummy").getSyntax()).isEqualTo(
-                CoreSchema.getDirectoryStringSyntax());
+                new SchemaBuilder(Schema.getCoreSchema())
+                    .setOption(DEFAULT_SYNTAX_OID, getDirectoryStringSyntax().getOID())
+                    .toSchema().asNonStrictSchema();
+        assertThat(schema.getDefaultSyntax()).isEqualTo(getDirectoryStringSyntax());
+        assertThat(schema.getAttributeType("dummy").getSyntax()).isEqualTo(getDirectoryStringSyntax());
     }
 
     @Test
@@ -2038,8 +2039,9 @@
     @Test
     public void testOverrideMatchingRule() {
         final Schema schema =
-                new SchemaBuilder(Schema.getCoreSchema()).defaultMatchingRule(
-                        CoreSchema.getCaseIgnoreMatchingRule()).toSchema().asNonStrictSchema();
+                new SchemaBuilder(Schema.getCoreSchema())
+                    .setOption(DEFAULT_MATCHING_RULE_OID, getCaseIgnoreMatchingRule().getOID())
+                    .toSchema().asNonStrictSchema();
         assertThat(schema.getDefaultMatchingRule()).isEqualTo(
                 CoreSchema.getCaseIgnoreMatchingRule());
         assertThat(schema.getAttributeType("dummy").getEqualityMatchingRule()).isEqualTo(
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java
index a483b0c..b86b813 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaCompatTest.java
@@ -30,6 +30,8 @@
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+
 /**
  * Tests schema compatibility options.
  */
@@ -73,11 +75,9 @@
      */
     @Test(dataProvider = "validAttributeDescriptions")
     public void testValidAttributeDescriptions(String atd, boolean allowIllegalCharacters) {
-        SchemaBuilder builder =
-                new SchemaBuilder(Schema.getCoreSchema())
-                        .allowMalformedNamesAndOptions(allowIllegalCharacters);
-        Schema schema = builder.toSchema().asNonStrictSchema();
-        AttributeDescription.valueOf(atd, schema);
+        SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema())
+            .setOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS, allowIllegalCharacters);
+        AttributeDescription.valueOf(atd, builder.toSchema().asNonStrictSchema());
     }
 
     /**
@@ -113,14 +113,11 @@
      *            {@code true} if the attribute description requires the
      *            compatibility option to be set.
      */
-    @Test(dataProvider = "invalidAttributeDescriptions",
-            expectedExceptions = LocalizedIllegalArgumentException.class)
+    @Test(dataProvider = "invalidAttributeDescriptions", expectedExceptions = LocalizedIllegalArgumentException.class)
     public void testInvalidAttributeDescriptions(String atd, boolean allowIllegalCharacters) {
-        SchemaBuilder builder =
-                new SchemaBuilder(Schema.getCoreSchema())
-                        .allowMalformedNamesAndOptions(allowIllegalCharacters);
-        Schema schema = builder.toSchema().asNonStrictSchema();
-        AttributeDescription.valueOf(atd, schema);
+        SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
+        builder.setOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS, allowIllegalCharacters);
+        AttributeDescription.valueOf(atd, builder.toSchema().asNonStrictSchema());
     }
 
     private static final Syntax ATD_SYNTAX = CoreSchema.getAttributeTypeDescriptionSyntax();
@@ -206,12 +203,10 @@
      *            {@code true} if the element requires the compatibility option
      *            to be set.
      */
-    @Test(dataProvider = "invalidSchemaElements",
-            expectedExceptions = LocalizedIllegalArgumentException.class)
-    public void testInvalidSchemaBuilderElementParsers(String element, Syntax syntax,
-            boolean allowIllegalCharacters) {
-        SchemaBuilder builder =
-                new SchemaBuilder().allowMalformedNamesAndOptions(allowIllegalCharacters);
+    @Test(dataProvider = "invalidSchemaElements", expectedExceptions = LocalizedIllegalArgumentException.class)
+    public void testInvalidSchemaBuilderElementParsers(String element, Syntax syntax, boolean allowIllegalCharacters) {
+        SchemaBuilder builder = new SchemaBuilder();
+        builder.setOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS, allowIllegalCharacters);
 
         if (syntax == ATD_SYNTAX) {
             builder.addAttributeType(element, false);
@@ -301,10 +296,9 @@
      *            to be set.
      */
     @Test(dataProvider = "validSchemaElements")
-    public void testValidSchemaBuilderElementParsers(String element, Syntax syntax,
-            boolean allowIllegalCharacters) {
-        SchemaBuilder builder =
-                new SchemaBuilder().allowMalformedNamesAndOptions(allowIllegalCharacters);
+    public void testValidSchemaBuilderElementParsers(String element, Syntax syntax, boolean allowIllegalCharacters) {
+        SchemaBuilder builder = new SchemaBuilder();
+        builder.setOption(ALLOW_MALFORMED_NAMES_AND_OPTIONS, allowIllegalCharacters);
 
         if (syntax == ATD_SYNTAX) {
             builder.addAttributeType(element, false);
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaOptionsTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaOptionsTestCase.java
new file mode 100644
index 0000000..6f5b180
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaOptionsTestCase.java
@@ -0,0 +1,83 @@
+/*
+ * 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 2013-2014 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap.schema;
+
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+
+import org.forgerock.opendj.ldap.Option;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+/**
+ * Tests the {@link SchemaOptions} class.
+ */
+public class SchemaOptionsTestCase extends AbstractSchemaTestCase {
+    private static final int TEST_OPTION_DEFAULT_VALUE = 42;
+    private static final Option<Integer> TEST_OPTION = Option.of(Integer.class, TEST_OPTION_DEFAULT_VALUE);
+
+    @DataProvider
+    private Object[][] defaultSchemaOptions() {
+        return new Object[][] {
+            { ALLOW_MALFORMED_CERTIFICATES },
+            { ALLOW_MALFORMED_JPEG_PHOTOS },
+            { ALLOW_MALFORMED_NAMES_AND_OPTIONS },
+            { ALLOW_NON_STANDARD_TELEPHONE_NUMBERS },
+            { ALLOW_ZERO_LENGTH_DIRECTORY_STRINGS },
+            { STRICT_FORMAT_FOR_COUNTRY_STRINGS },
+            { STRIP_UPPER_BOUND_FOR_ATTRIBUTE_TYPE }};
+    }
+
+    @Test(dataProvider = "defaultSchemaOptions")
+    public void testDefaultSchemaOptions(Option<?> option) {
+        assertThat(new SchemaBuilder().getOptions().get(option)).isEqualTo(option.getValue(null));
+    }
+
+    @Test
+    public void testAddSchemaOption() {
+        assertThat(newSchemaBuilder().getOptions().get(TEST_OPTION)).isEqualTo(TEST_OPTION_DEFAULT_VALUE);
+    }
+
+    @Test
+    public void testSetSchemaOption() {
+        assertThat(newSchemaBuilder().setOption(TEST_OPTION, 0).getOptions().get(TEST_OPTION)).isEqualTo(0);
+    }
+
+    @Test
+    public void testSchemaOptionsCopy() {
+        final SchemaOptions copiedOptions = SchemaOptions.copyOf(newSchemaBuilder().getOptions());
+        assertThat(copiedOptions.get(TEST_OPTION)).isEqualTo(TEST_OPTION_DEFAULT_VALUE);
+    }
+
+    @Test(expectedExceptions = UnsupportedOperationException.class)
+    public void testAsReadOnlyOptions() {
+        SchemaOptions.unmodifiable(new SchemaBuilder().getOptions()).set(ALLOW_MALFORMED_CERTIFICATES, false);
+    }
+
+    private SchemaBuilder newSchemaBuilder() {
+        return new SchemaBuilder().setOption(TEST_OPTION, TEST_OPTION_DEFAULT_VALUE);
+    }
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java
index 544f1a1..54fee40 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SyntaxTestCase.java
@@ -27,11 +27,12 @@
 
 package org.forgerock.opendj.ldap.schema;
 
-import static org.fest.assertions.Assertions.assertThat;
-
 import org.forgerock.opendj.ldap.schema.Syntax.Builder;
 import org.testng.annotations.Test;
 
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+
 /**
  * This class tests the Syntax class.
  */
@@ -198,7 +199,7 @@
     public final void testBuilderAllowsNoSyntaxCaseWhereDefaultSyntaxIsChanged() {
         // @formatter:off
         final Schema schema = new SchemaBuilder(Schema.getCoreSchema())
-            .defaultSyntax("1.3.6.1.4.1.1466.115.121.1.15")
+            .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")
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java
index d5a6d23..79a5ed6 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldif/LDIFTestCase.java
@@ -27,8 +27,6 @@
 
 package org.forgerock.opendj.ldif;
 
-import static org.fest.assertions.Assertions.assertThat;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -54,13 +52,18 @@
 import org.forgerock.opendj.ldap.requests.ModifyRequest;
 import org.forgerock.opendj.ldap.requests.Requests;
 import org.forgerock.opendj.ldap.requests.SearchRequest;
-import org.forgerock.opendj.ldap.schema.CoreSchema;
+import org.forgerock.opendj.ldap.schema.MatchingRule;
 import org.forgerock.opendj.ldap.schema.Schema;
 import org.forgerock.opendj.ldap.schema.SchemaBuilder;
+import org.forgerock.opendj.ldap.schema.Syntax;
 import org.testng.annotations.Test;
 
 import com.forgerock.opendj.ldap.CoreMessages;
 
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.opendj.ldap.schema.CoreSchema.*;
+import static org.forgerock.opendj.ldap.schema.SchemaOptions.*;
+
 /**
  * This class tests the LDIF functionality.
  */
@@ -550,10 +553,7 @@
      */
     @Test
     public final void testSearchForEntryInLDIFUsingIgnoreMatchingRuleSucceedWithLowerCaseFilter() throws IOException {
-        Schema schema =
-                new SchemaBuilder(Schema.getCoreSchema()).defaultMatchingRule(
-                        CoreSchema.getCaseIgnoreMatchingRule()).defaultSyntax(
-                        CoreSchema.getDirectoryStringSyntax()).toSchema().asNonStrictSchema();
+        Schema schema = newSchemaBuilder(getCaseIgnoreMatchingRule(), getDirectoryStringSyntax());
 
         // @formatter:off
         final LDIFEntryReader input = new LDIFEntryReader(
@@ -584,10 +584,7 @@
      */
     @Test(expectedExceptions = NoSuchElementException.class)
     public final void testSearchForEntryInLDIFUsingExactMatchingRuleFailsWithLowerCaseFilter() throws IOException {
-        Schema schema =
-                new SchemaBuilder(Schema.getCoreSchema()).defaultMatchingRule(
-                        CoreSchema.getCaseExactMatchingRule()).defaultSyntax(
-                        CoreSchema.getDirectoryStringSyntax()).toSchema().asNonStrictSchema();
+        Schema schema = newSchemaBuilder(getCaseExactMatchingRule(), getDirectoryStringSyntax());
 
         // @formatter:off
         final LDIFEntryReader input = new LDIFEntryReader(
@@ -613,10 +610,7 @@
      */
     @Test
     public final void testSearchForEntryInLDIFUsingExactMatchingRuleSucceedWithRightFilter() throws IOException {
-        Schema schema =
-                new SchemaBuilder(Schema.getCoreSchema()).defaultMatchingRule(
-                        CoreSchema.getCaseExactMatchingRule()).defaultSyntax(
-                        CoreSchema.getDirectoryStringSyntax()).toSchema().asNonStrictSchema();
+        Schema schema = newSchemaBuilder(getCaseExactMatchingRule(), getDirectoryStringSyntax());
 
         // @formatter:off
         final LDIFEntryReader input = new LDIFEntryReader(
@@ -640,6 +634,13 @@
         input.close();
     }
 
+    private Schema newSchemaBuilder(MatchingRule defaultMatchingRule, Syntax defaultSyntax) {
+        return new SchemaBuilder(Schema.getCoreSchema())
+            .setOption(DEFAULT_MATCHING_RULE_OID, defaultMatchingRule.getOID())
+            .setOption(DEFAULT_SYNTAX_OID, defaultSyntax.getOID())
+            .toSchema().asNonStrictSchema();
+    }
+
     /**
      * Verifying LDIF collection reader.
      *

--
Gitblit v1.10.0