From 329c50b351326ecd9c153635beaad5ecb05a0e6d Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Thu, 15 Jan 2015 15:51:02 +0000
Subject: [PATCH] OPENDJ-1722 (CR-5788) Add fluent builder for AttributeType

---
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java                |  234 ++-----
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java        |   38 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java                |   16 
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java                |  368 ++++++++++-
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeBuilderTestCase.java |  186 ++++++
 opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java               |  915 +++++++++++++++++++---------
 opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java            |   71 +
 7 files changed, 1,284 insertions(+), 544 deletions(-)

diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
index 4951e6a..25d7daf 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/AttributeType.java
@@ -22,16 +22,23 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
- *      Portions copyright 2011-2013 ForgeRock AS.
+ *      Portions copyright 2011-2015 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.SCHEMA_PROPERTY_APPROX_RULE;
+import static java.util.Arrays.*;
 
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
+import static com.forgerock.opendj.util.StaticUtils.*;
+
+import java.util.Collection;
 import java.util.Collections;
 import java.util.Iterator;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -52,6 +59,295 @@
  */
 public final class AttributeType extends SchemaElement implements Comparable<AttributeType> {
 
+    /** A fluent API for incrementally constructing attribute type. */
+    public static final class Builder extends SchemaElementBuilder<Builder> {
+        private String oid;
+        private final List<String> names = new LinkedList<String>();
+        private AttributeUsage attributeUsage;
+        private boolean isCollective;
+        private boolean isNoUserModification;
+        private boolean isObsolete;
+        private boolean isSingleValue;
+        private String approximateMatchingRuleOID;
+        private String equalityMatchingRuleOID;
+        private String orderingMatchingRuleOID;
+        private String substringMatchingRuleOID;
+        private String superiorTypeOID;
+        private String syntaxOID;
+
+        Builder(final AttributeType at, final SchemaBuilder builder) {
+            super(builder, at);
+            this.oid = at.oid;
+            this.attributeUsage = at.attributeUsage;
+            this.isCollective = at.isCollective;
+            this.isNoUserModification = at.isNoUserModification;
+            this.isObsolete = at.isObsolete;
+            this.isSingleValue = at.isSingleValue;
+            this.names.addAll(at.names);
+            this.approximateMatchingRuleOID = at.approximateMatchingRuleOID;
+            this.equalityMatchingRuleOID = at.equalityMatchingRuleOID;
+            this.orderingMatchingRuleOID = at.orderingMatchingRuleOID;
+            this.substringMatchingRuleOID = at.substringMatchingRuleOID;
+            this.superiorTypeOID = at.superiorTypeOID;
+            this.syntaxOID = at.syntaxOID;
+        }
+
+        Builder(final String oid, final SchemaBuilder builder) {
+            super(builder);
+            this.oid = oid;
+        }
+
+        /**
+         * Adds this attribute type to the schema, throwing an
+         * {@code ConflictingSchemaElementException} if there is an existing
+         * attribute type with the same numeric OID.
+         *
+         * @return The parent schema builder.
+         * @throws ConflictingSchemaElementException
+         *             If there is an existing attribute type with the same
+         *             numeric OID.
+         */
+        public SchemaBuilder addToSchema() {
+            return getSchemaBuilder().addAttributeType(new AttributeType(this), false);
+        }
+
+        /**
+         * Adds this attribute type to the schema overwriting any existing
+         * attribute type with the same numeric OID.
+         *
+         * @return The parent schema builder.
+         */
+        public SchemaBuilder addToSchemaOverwrite() {
+            return getSchemaBuilder().addAttributeType(new AttributeType(this), true);
+        }
+
+        /**
+         * Sets the matching rule that should be used for approximate matching
+         * with this attribute type.
+         *
+         * @param approximateMatchingRuleOID
+         *            The matching rule OID.
+         * @return This builder.
+         */
+        public Builder approximateMatchingRule(String approximateMatchingRuleOID) {
+            this.approximateMatchingRuleOID = approximateMatchingRuleOID;
+            return this;
+        }
+
+        /**
+         * Specifies whether this attribute type is "collective".
+         *
+         * @param isCollective
+         *            {@code true} if this attribute type is "collective".
+         * @return This builder.
+         */
+        public Builder collective(boolean isCollective) {
+            this.isCollective = isCollective;
+            return this;
+        }
+
+        @Override
+        public Builder description(String description) {
+            return description0(description);
+        }
+
+        /**
+         * Sets the matching rule that should be used for equality matching with
+         * this attribute type.
+         *
+         * @param equalityMatchingRuleOID
+         *            The matching rule OID.
+         * @return This builder.
+         */
+        public Builder equalityMatchingRule(String equalityMatchingRuleOID) {
+            this.equalityMatchingRuleOID = equalityMatchingRuleOID;
+            return this;
+        }
+
+        @Override
+        public Builder extraProperties(Map<String, List<String>> extraProperties) {
+            return extraProperties0(extraProperties);
+        }
+
+        @Override
+        public Builder extraProperties(String extensionName, String... extensionValues) {
+            return extraProperties0(extensionName, extensionValues);
+        }
+
+        @Override
+        Builder getThis() {
+            return this;
+        }
+
+        /**
+         * Adds the provided user friendly names.
+         *
+         * @param names
+         *            The user friendly names.
+         * @return This builder.
+         */
+        public Builder names(final Collection<String> names) {
+            this.names.addAll(names);
+            return this;
+        }
+
+        /**
+         * Adds the provided user friendly names.
+         *
+         * @param names
+         *            The user friendly names.
+         * @return This builder.
+         */
+        public Builder names(final String... names) {
+            return names(asList(names));
+        }
+
+        /**
+         * Specifies whether this attribute type is "no-user-modification".
+         *
+         * @param isNoUserModification
+         *            {@code true} if this attribute type is
+         *            "no-user-modification"
+         * @return This builder.
+         */
+        public Builder noUserModification(boolean isNoUserModification) {
+            this.isNoUserModification = isNoUserModification;
+            return this;
+        }
+
+        /**
+         * Specifies whether this schema element is obsolete.
+         *
+         * @param isObsolete
+         *            {@code true} if this schema element is obsolete (default
+         *            is {@code false}).
+         * @return This builder.
+         */
+        public Builder obsolete(final boolean isObsolete) {
+            this.isObsolete = isObsolete;
+            return this;
+        }
+
+        /**
+         * Sets the numeric OID which uniquely identifies this attribute type.
+         *
+         * @param oid
+         *            The numeric OID.
+         * @return This builder.
+         */
+        public Builder oid(final String oid) {
+            this.oid = oid;
+            return this;
+        }
+
+        /**
+         * Sets the matching rule that should be used for ordering with this
+         * attribute type.
+         *
+         * @param orderingMatchingRuleOID
+         *            The matching rule OID.
+         * @return This Builder.
+         */
+        public Builder orderingMatchingRule(String orderingMatchingRuleOID) {
+            this.orderingMatchingRuleOID = orderingMatchingRuleOID;
+            return this;
+        }
+
+        @Override
+        public Builder removeAllExtraProperties() {
+            return removeAllExtraProperties0();
+        }
+
+        /**
+         * Removes all user defined names.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllNames() {
+            this.names.clear();
+            return this;
+        }
+
+        @Override
+        public Builder removeExtraProperty(String extensionName, String... extensionValues) {
+            return removeExtraProperty0(extensionName, extensionValues);
+        }
+
+        /**
+         * Removes the provided user defined name.
+         *
+         * @param name
+         *            The user defined name to be removed.
+         * @return This builder.
+         */
+        public Builder removeName(String name) {
+            this.names.remove(name);
+            return this;
+        }
+
+        /**
+         * Specifies whether this attribute type is declared "single-value".
+         *
+         * @param isSingleValue
+         *            {@code true} if this attribute type is declared
+         *            "single-value".
+         * @return This builder.
+         */
+        public Builder singleValue(boolean isSingleValue) {
+            this.isSingleValue = isSingleValue;
+            return this;
+        }
+
+        /**
+         * Sets the matching rule that should be used for substring matching
+         * with this attribute type.
+         *
+         * @param substringMatchingRuleOID
+         *            The matching rule OID.
+         * @return This builder.
+         */
+        public Builder substringMatchingRule(String substringMatchingRuleOID) {
+            this.substringMatchingRuleOID = substringMatchingRuleOID;
+            return this;
+        }
+
+        /**
+         * Sets the superior type for this attribute type.
+         *
+         * @param superiorTypeOID
+         *            The superior type OID.
+         * @return This builder.
+         */
+        public Builder superiorType(String superiorTypeOID) {
+            this.superiorTypeOID = superiorTypeOID;
+            return this;
+        }
+
+        /**
+         * Sets the syntax for this attribute type.
+         *
+         * @param syntaxOID
+         *            The syntax OID.
+         * @return This builder.
+         */
+        public Builder syntax(String syntaxOID) {
+            this.syntaxOID = syntaxOID;
+            return this;
+        }
+
+        /**
+         * Sets the usage indicator for this attribute type.
+         *
+         * @param attributeUsage
+         *            The attribute usage.
+         * @return This builder.
+         */
+        public Builder usage(AttributeUsage attributeUsage) {
+            this.attributeUsage = attributeUsage;
+            return this;
+        }
+    }
+
     /** The approximate matching rule for this attribute type. */
     private final String approximateMatchingRuleOID;
 
@@ -64,9 +360,7 @@
     /** Indicates whether this attribute type is declared "collective". */
     private final boolean isCollective;
 
-    /**
-     * Indicates whether this attribute type is declared "no-user-modification".
-     */
+    /** Indicates whether this attribute type is declared "no-user-modification". */
     private final boolean isNoUserModification;
 
     /** Indicates whether this definition is declared "obsolete". */
@@ -126,34 +420,28 @@
     /** The indicates whether or not validation failed. */
     private boolean isValid;
 
-    AttributeType(final String oid, final List<String> names, final String description,
-            final boolean obsolete, final String superiorType, final String equalityMatchingRule,
-            final String orderingMatchingRule, final String substringMatchingRule,
-            final String approximateMatchingRule, final String syntax, final boolean singleValue,
-            final boolean collective, final boolean noUserModification,
-            final AttributeUsage attributeUsage, final Map<String, List<String>> extraProperties,
-            final String definition) {
-        super(description, extraProperties, definition);
+    private AttributeType(Builder builder) {
+        super(builder);
+        Reject.ifTrue(builder.oid == null || builder.oid.isEmpty(), "An OID must be specified.");
+        Reject.ifTrue(builder.superiorTypeOID == null && builder.syntaxOID == null,
+                "Superior type and/or Syntax must not be null");
 
-        Reject.ifNull(oid, names, attributeUsage);
-        Reject.ifFalse(superiorType != null || syntax != null, "superiorType and/or syntax must not be null");
-
-        this.oid = oid;
-        this.names = names;
-        this.isObsolete = obsolete;
-        this.superiorTypeOID = superiorType;
-        this.equalityMatchingRuleOID = equalityMatchingRule;
-        this.orderingMatchingRuleOID = orderingMatchingRule;
-        this.substringMatchingRuleOID = substringMatchingRule;
-        this.approximateMatchingRuleOID = approximateMatchingRule;
-        this.syntaxOID = syntax;
-        this.isSingleValue = singleValue;
-        this.isCollective = collective;
-        this.isNoUserModification = noUserModification;
-        this.attributeUsage = attributeUsage;
-        this.isObjectClassType = "2.5.4.0".equals(oid);
-        this.isPlaceHolder = false;
-        this.normalizedName = StaticUtils.toLowerCase(getNameOrOID());
+        oid = builder.oid;
+        names = unmodifiableCopyOfList(builder.names);
+        attributeUsage = builder.attributeUsage;
+        isCollective = builder.isCollective;
+        isNoUserModification = builder.isNoUserModification;
+        isObjectClassType = "2.5.4.0".equals(oid);
+        isObsolete = builder.isObsolete;
+        isSingleValue = builder.isSingleValue;
+        approximateMatchingRuleOID = builder.approximateMatchingRuleOID;
+        equalityMatchingRuleOID = builder.equalityMatchingRuleOID;
+        orderingMatchingRuleOID = builder.orderingMatchingRuleOID;
+        substringMatchingRuleOID = builder.substringMatchingRuleOID;
+        superiorTypeOID = builder.superiorTypeOID;
+        syntaxOID = builder.syntaxOID;
+        isPlaceHolder = false;
+        normalizedName = toLowerCase(getNameOrOID());
     }
 
     /**
@@ -187,7 +475,7 @@
         this.isSingleValue = false;
         this.isCollective = false;
         this.isNoUserModification = false;
-        this.attributeUsage = AttributeUsage.USER_APPLICATIONS;
+        this.attributeUsage = null;
         this.isObjectClassType = false;
         this.isPlaceHolder = true;
         this.normalizedName = StaticUtils.toLowerCase(getNameOrOID());
@@ -307,7 +595,6 @@
      * @return The OID for this schema definition.
      */
     public String getOID() {
-
         return oid;
     }
 
@@ -358,7 +645,7 @@
      * @return The usage indicator for this attribute type.
      */
     public AttributeUsage getUsage() {
-        return attributeUsage;
+        return attributeUsage != null ? attributeUsage : AttributeUsage.USER_APPLICATIONS;
     }
 
     /**
@@ -453,7 +740,7 @@
      *         {@code false} if not.
      */
     public boolean isOperational() {
-        return attributeUsage.isOperational();
+        return getUsage().isOperational();
     }
 
     /**
@@ -550,13 +837,6 @@
         }
     }
 
-    AttributeType duplicate() {
-        return new AttributeType(oid, names, getDescription(), isObsolete, superiorTypeOID,
-                equalityMatchingRuleOID, orderingMatchingRuleOID, substringMatchingRuleOID,
-                approximateMatchingRuleOID, syntaxOID, isSingleValue, isCollective,
-                isNoUserModification, attributeUsage, getExtraProperties(), toString());
-    }
-
     @Override
     void toStringContent(final StringBuilder buffer) {
         buffer.append(oid);
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
index 9e3bbc3..c502237 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
@@ -33,7 +33,6 @@
 import static org.forgerock.opendj.ldap.schema.TimeBasedMatchingRulesImpl.*;
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -70,8 +69,6 @@
     private static final Map<String, List<String>> OPENDJ_ORIGIN = Collections.singletonMap(
             SCHEMA_PROPERTY_ORIGIN, Collections.singletonList("OpenDJ Directory Server"));
 
-    private static final String EMPTY_STRING = "".intern();
-
     private static final Schema SINGLETON;
 
     /**
@@ -306,15 +303,25 @@
     }
 
     private static void addRFC3045(final SchemaBuilder builder) {
-        builder.addAttributeType("1.3.6.1.1.4", Collections.singletonList("vendorName"),
-                EMPTY_STRING, false, null, EMR_CASE_EXACT_IA5_OID, null, null, null,
-                SYNTAX_DIRECTORY_STRING_OID, true, false, true, AttributeUsage.DSA_OPERATION,
-                RFC3045_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.1.4")
+               .names("vendorName")
+               .equalityMatchingRule(EMR_CASE_EXACT_IA5_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC3045_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.1.5", Collections.singletonList("vendorVersion"),
-                EMPTY_STRING, false, null, EMR_CASE_EXACT_IA5_OID, null, null, null,
-                SYNTAX_DIRECTORY_STRING_OID, true, false, true, AttributeUsage.DSA_OPERATION,
-                RFC3045_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.1.5")
+               .names("vendorVersion")
+               .equalityMatchingRule(EMR_CASE_EXACT_IA5_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC3045_ORIGIN)
+               .addToSchema();
     }
 
     private static void addRFC3112(final SchemaBuilder builder) {
@@ -325,15 +332,25 @@
                 .description(EMR_AUTH_PASSWORD_EXACT_DESCRIPTION).syntaxOID(SYNTAX_AUTH_PASSWORD_OID)
                 .extraProperties(RFC3112_ORIGIN).implementation(new AuthPasswordExactEqualityMatchingRuleImpl())
                 .addToSchema();
-        builder.addAttributeType("1.3.6.1.4.1.4203.1.3.3", Collections
-                .singletonList("supportedAuthPasswordSchemes"),
-                "supported password storage schemes", false, null, EMR_CASE_EXACT_IA5_OID, null,
-                null, null, SYNTAX_IA5_STRING_OID, false, false, false,
-                AttributeUsage.DSA_OPERATION, RFC3112_ORIGIN, false);
-        builder.addAttributeType("1.3.6.1.4.1.4203.1.3.4", Collections
-                .singletonList("authPassword"), "password authentication information", false, null,
-                EMR_AUTH_PASSWORD_EXACT_OID, null, null, null, SYNTAX_AUTH_PASSWORD_OID, false,
-                false, false, AttributeUsage.USER_APPLICATIONS, RFC3112_ORIGIN, false);
+
+        builder.buildAttributeType("1.3.6.1.4.1.4203.1.3.3")
+               .names("supportedAuthPasswordSchemes")
+               .description("supported password storage schemes")
+               .equalityMatchingRule(EMR_CASE_EXACT_IA5_OID)
+               .syntax(SYNTAX_IA5_STRING_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC3112_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("1.3.6.1.4.1.4203.1.3.4")
+               .names("authPassword")
+               .description("password authentication information")
+               .equalityMatchingRule(EMR_AUTH_PASSWORD_EXACT_OID)
+               .syntax(SYNTAX_AUTH_PASSWORD_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC3112_ORIGIN)
+               .addToSchema();
+
         builder.buildObjectClass("1.3.6.1.4.1.4203.1.4.7")
                 .names("authPasswordObject")
                 .type(AUXILIARY)
@@ -344,201 +361,354 @@
     }
 
     private static void addRFC4519(final SchemaBuilder builder) {
-        builder.addAttributeType("2.5.4.15", Collections.singletonList("businessCategory"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.15")
+               .names("businessCategory")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.41", Collections.singletonList("name"), EMPTY_STRING,
-                false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.41")
+               .names("name")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.6", Arrays.asList("c", "countryName"), EMPTY_STRING, false,
-                "name", null, null, null, null, SYNTAX_COUNTRY_STRING_OID, true, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.6")
+               .names("c", "countryName")
+               .superiorType("name")
+               .syntax(SYNTAX_COUNTRY_STRING_OID)
+               .singleValue(true)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.3", Arrays.asList("cn", "commonName"), EMPTY_STRING, false,
-                "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.3")
+               .names("cn", "commonName")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("0.9.2342.19200300.100.1.25", Arrays.asList("dc",
-                "domainComponent"), EMPTY_STRING, false, null, EMR_CASE_IGNORE_IA5_OID, null,
-                SMR_CASE_IGNORE_IA5_OID, null, SYNTAX_IA5_STRING_OID, true, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("0.9.2342.19200300.100.1.25")
+               .names("dc", "domainComponent")
+               .equalityMatchingRule(EMR_CASE_IGNORE_IA5_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_IA5_OID)
+               .syntax(SYNTAX_IA5_STRING_OID)
+               .singleValue(true)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.13", Collections.singletonList("description"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.13")
+               .names("description")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.27", Collections.singletonList("destinationIndicator"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_PRINTABLE_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.27")
+               .names("destinationIndicator")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_PRINTABLE_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.49", Collections.singletonList("distinguishedName"),
-                EMPTY_STRING, false, null, EMR_DN_OID, null, null, null, SYNTAX_DN_OID, false,
-                false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.49")
+               .names("distinguishedName")
+               .equalityMatchingRule(EMR_DN_OID)
+               .syntax(SYNTAX_DN_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.46", Collections.singletonList("dnQualifier"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, OMR_CASE_IGNORE_OID,
-                SMR_CASE_IGNORE_OID, null, SYNTAX_PRINTABLE_STRING_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.46")
+               .names("dnQualifier")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .orderingMatchingRule(OMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_PRINTABLE_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.47", Collections.singletonList("enhancedSearchGuide"),
-                EMPTY_STRING, false, null, null, null, null, null, SYNTAX_ENHANCED_GUIDE_OID,
-                false, false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.47")
+               .names("enhancedSearchGuide")
+               .syntax(SYNTAX_ENHANCED_GUIDE_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.23", Collections.singletonList("facsimileTelephoneNumber"),
-                EMPTY_STRING, false, null, null, null, null, null, SYNTAX_FAXNUMBER_OID, false,
-                false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.23")
+               .names("facsimileTelephoneNumber")
+               .syntax(SYNTAX_FAXNUMBER_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.44", Collections.singletonList("generationQualifier"),
-                EMPTY_STRING, false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.44")
+               .names("generationQualifier")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.42", Collections.singletonList("givenName"), EMPTY_STRING,
-                false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.42")
+               .names("givenName")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.51", Collections.singletonList("houseIdentifier"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.51")
+               .names("houseIdentifier")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.43", Collections.singletonList("initials"), EMPTY_STRING,
-                false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.43")
+               .names("initials")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.25", Collections.singletonList("internationalISDNNumber"),
-                EMPTY_STRING, false, null, EMR_NUMERIC_STRING_OID, null, SMR_NUMERIC_STRING_OID,
-                null, SYNTAX_NUMERIC_STRING_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.25")
+               .names("internationalISDNNumber")
+               .equalityMatchingRule(EMR_NUMERIC_STRING_OID)
+               .substringMatchingRule(SMR_NUMERIC_STRING_OID)
+               .syntax(SYNTAX_NUMERIC_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.7", Arrays.asList("l", "localityName"), EMPTY_STRING,
-                false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.7")
+               .names("l", "localityName")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.31", Collections.singletonList("member"), EMPTY_STRING,
-                false, "distinguishedName", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.31")
+               .names("member")
+               .superiorType("distinguishedName")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.10", Arrays.asList("o", "organizationName"), EMPTY_STRING,
-                false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.10")
+               .names("o", "organizationName")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.11", Arrays.asList("ou", "organizationalUnitName"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.11")
+               .names("ou", "organizationalUnitName")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.32", Collections.singletonList("owner"), EMPTY_STRING,
-                false, "distinguishedName", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.32")
+               .names("owner")
+               .superiorType("distinguishedName")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.19", Collections
-                .singletonList("physicalDeliveryOfficeName"), EMPTY_STRING, false, null,
-                EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null, SYNTAX_DIRECTORY_STRING_OID,
-                false, false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.19")
+               .names("physicalDeliveryOfficeName")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.16", Collections.singletonList("postalAddress"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.16")
+               .names("postalAddress")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.17", Collections.singletonList("postalCode"), EMPTY_STRING,
-                false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.17")
+               .names("postalCode")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.18", Collections.singletonList("postOfficeBox"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.18")
+               .names("postOfficeBox")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.28", Collections.singletonList("preferredDeliveryMethod"),
-                EMPTY_STRING, false, null, null, null, null, null, SYNTAX_DELIVERY_METHOD_OID,
-                true, false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.28")
+               .names("preferredDeliveryMethod")
+               .syntax(SYNTAX_DELIVERY_METHOD_OID)
+               .singleValue(true)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.26", Collections.singletonList("registeredAddress"),
-                EMPTY_STRING, false, "postalAddress", null, null, null, null,
-                SYNTAX_POSTAL_ADDRESS_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.26")
+               .names("registeredAddress")
+               .superiorType("postalAddress")
+               .syntax(SYNTAX_POSTAL_ADDRESS_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.33", Collections.singletonList("roleOccupant"),
-                EMPTY_STRING, false, "distinguishedName", null, null, null, null, null, false,
-                false, false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.33")
+               .names("roleOccupant")
+               .superiorType("distinguishedName")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.14", Collections.singletonList("searchGuide"),
-                EMPTY_STRING, false, null, null, null, null, null, SYNTAX_GUIDE_OID, false, false,
-                false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.14")
+               .names("searchGuide")
+               .syntax(SYNTAX_GUIDE_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.34", Collections.singletonList("seeAlso"), EMPTY_STRING,
-                false, "distinguishedName", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.34")
+               .names("seeAlso")
+               .superiorType("distinguishedName")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.5", Collections.singletonList("serialNumber"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_PRINTABLE_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.5")
+               .names("serialNumber")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_PRINTABLE_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.4", Arrays.asList("sn", "surname"), EMPTY_STRING, false,
-                "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.4")
+               .names("sn", "surname")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.8", Arrays.asList("st", "stateOrProvinceName"),
-                EMPTY_STRING, false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.8")
+               .names("st", "stateOrProvinceName")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.9", Arrays.asList("street", "streetAddress"), EMPTY_STRING,
-                false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.9")
+               .names("street", "streetAddress")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.20", Collections.singletonList("telephoneNumber"),
-                EMPTY_STRING, false, null, EMR_TELEPHONE_OID, null, SMR_TELEPHONE_OID, null,
-                SYNTAX_TELEPHONE_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.20")
+               .names("telephoneNumber")
+               .equalityMatchingRule(EMR_TELEPHONE_OID)
+               .substringMatchingRule(SMR_TELEPHONE_OID)
+               .syntax(SYNTAX_TELEPHONE_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.22",
-                Collections.singletonList("teletexTerminalIdentifier"), EMPTY_STRING, false, null,
-                null, null, null, null, SYNTAX_TELETEX_TERM_ID_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.22")
+               .names("teletexTerminalIdentifier")
+               .syntax(SYNTAX_TELETEX_TERM_ID_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.21", Collections.singletonList("telexNumber"),
-                EMPTY_STRING, false, null, null, null, null, null, SYNTAX_TELEX_OID, false, false,
-                false, AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.21")
+               .names("telexNumber")
+               .syntax(SYNTAX_TELEX_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.12", Collections.singletonList("title"), EMPTY_STRING,
-                false, "name", null, null, null, null, null, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.12")
+               .names("title")
+               .superiorType("name")
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("0.9.2342.19200300.100.1.1", Arrays.asList("uid", "userid"),
-                EMPTY_STRING, false, null, EMR_CASE_IGNORE_OID, null, SMR_CASE_IGNORE_OID, null,
-                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("0.9.2342.19200300.100.1.1")
+               .names("uid", "userid")
+               .equalityMatchingRule(EMR_CASE_IGNORE_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.50", Collections.singletonList("uniqueMember"),
-                EMPTY_STRING, false, null, EMR_UNIQUE_MEMBER_OID, null, null, null,
-                SYNTAX_NAME_AND_OPTIONAL_UID_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.50")
+               .names("uniqueMember")
+               .equalityMatchingRule(EMR_UNIQUE_MEMBER_OID)
+               .syntax(SYNTAX_NAME_AND_OPTIONAL_UID_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.35", Collections.singletonList("userPassword"),
-                EMPTY_STRING, false, null, EMR_OCTET_STRING_OID, null, null, null,
-                SYNTAX_OCTET_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.35")
+               .names("userPassword")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_OCTET_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.24", Collections.singletonList("x121Address"),
-                EMPTY_STRING, false, null, EMR_NUMERIC_STRING_OID, null, SMR_NUMERIC_STRING_OID,
-                null, SYNTAX_NUMERIC_STRING_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.24")
+               .names("x121Address")
+               .equalityMatchingRule(EMR_NUMERIC_STRING_OID)
+               .substringMatchingRule(SMR_NUMERIC_STRING_OID)
+               .syntax(SYNTAX_NUMERIC_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.45", Collections.singletonList("x500UniqueIdentifier"),
-                EMPTY_STRING, false, null, EMR_BIT_STRING_OID, null, null, null,
-                SYNTAX_BIT_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
-                RFC4519_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.45")
+               .names("x500UniqueIdentifier")
+               .equalityMatchingRule(EMR_BIT_STRING_OID)
+               .syntax(SYNTAX_BIT_STRING_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4519_ORIGIN)
+               .addToSchema();
 
         builder.buildObjectClass("2.5.6.11")
                 .names("applicationProcess")
@@ -690,34 +860,68 @@
                 .syntaxOID(SYNTAX_CERTIFICATE_EXACT_ASSERTION_OID).extraProperties(RFC4523_ORIGIN)
                 .implementation(new CertificateExactMatchingRuleImpl()).addToSchema();
 
-        builder.addAttributeType("2.5.4.36", Collections.singletonList("userCertificate"),
-                "X.509 user certificate", false, null, EMR_CERTIFICATE_EXACT_OID, null,
-                null, null, SYNTAX_CERTIFICATE_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.37", Collections.singletonList("cACertificate"),
-                "X.509 CA certificate", false, null, EMR_CERTIFICATE_EXACT_OID, null,
-                null, null, SYNTAX_CERTIFICATE_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.38", Collections.singletonList("authorityRevocationList"),
-                "X.509 authority revocation list", false, null, EMR_OCTET_STRING_OID, null,
-                null, null, SYNTAX_CERTLIST_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.39", Collections.singletonList("certificateRevocationList"),
-                "X.509 certificate revocation list", false, null, EMR_OCTET_STRING_OID, null,
-                null, null, SYNTAX_CERTLIST_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.40", Collections.singletonList("crossCertificatePair"),
-                "X.509 cross certificate pair", false, null, EMR_OCTET_STRING_OID, null,
-                null, null, SYNTAX_CERTPAIR_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.52", Collections.singletonList("supportedAlgorithms"),
-                "X.509 supported algorithms", false, null, EMR_OCTET_STRING_OID, null,
-                null, null, SYNTAX_SUPPORTED_ALGORITHM_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
-        builder.addAttributeType("2.5.4.53", Collections.singletonList("deltaRevocationList"),
-                "X.509 delta revocation list", false, null, EMR_OCTET_STRING_OID, null,
-                null, null, SYNTAX_CERTLIST_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.36")
+               .names("userCertificate")
+               .description("X.509 user certificate")
+               .equalityMatchingRule(EMR_CERTIFICATE_EXACT_OID)
+               .syntax(SYNTAX_CERTIFICATE_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.37")
+               .names("cACertificate")
+               .description("X.509 CA certificate")
+               .equalityMatchingRule(EMR_CERTIFICATE_EXACT_OID)
+               .syntax(SYNTAX_CERTIFICATE_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.38")
+               .names("authorityRevocationList")
+               .description("X.509 authority revocation list")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_CERTLIST_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.39")
+               .names("certificateRevocationList")
+               .description("X.509 certificate revocation list")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_CERTLIST_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.40")
+               .names("crossCertificatePair")
+               .description("X.509 cross certificate pair")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_CERTPAIR_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.52")
+               .names("supportedAlgorithms")
+               .description("X.509 supported algorithms")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_SUPPORTED_ALGORITHM_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
+
+        builder.buildAttributeType("2.5.4.53")
+               .names("deltaRevocationList")
+               .description("X.509 delta revocation list")
+               .equalityMatchingRule(EMR_OCTET_STRING_OID)
+               .syntax(SYNTAX_CERTLIST_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4523_ORIGIN)
+               .addToSchema();
 
         builder.buildObjectClass("2.5.6.21")
                 .names("pkiUser")
@@ -803,17 +1007,30 @@
         builder.buildMatchingRule(OMR_UUID_OID).names(OMR_UUID_NAME).syntaxOID(SYNTAX_UUID_OID)
                 .extraProperties(RFC4530_ORIGIN).implementation(new UUIDOrderingMatchingRuleImpl())
                 .addToSchema();
-        builder.addAttributeType("1.3.6.1.1.16.4", Collections.singletonList("entryUUID"),
-                "UUID of the entry", false, null, EMR_UUID_OID, OMR_UUID_OID, null, null,
-                SYNTAX_UUID_OID, true, false, true, AttributeUsage.DIRECTORY_OPERATION,
-                RFC4530_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.1.16.4")
+               .names("entryUUID")
+               .description("UUID of the entry")
+               .equalityMatchingRule(EMR_UUID_OID)
+               .orderingMatchingRule(OMR_UUID_OID)
+               .syntax(SYNTAX_UUID_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4530_ORIGIN)
+               .addToSchema();
     }
 
     private static void addRFC5020(final SchemaBuilder builder) {
-        builder.addAttributeType("1.3.6.1.1.20", Collections.singletonList("entryDN"),
-                "DN of the entry", false, null, EMR_DN_OID, null, null, null,
-                SYNTAX_DN_OID, true, false, true, AttributeUsage.DIRECTORY_OPERATION,
-                RFC5020_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.1.20")
+               .names("entryDN")
+               .description("DN of the entry")
+               .equalityMatchingRule(EMR_DN_OID)
+               .syntax(SYNTAX_DN_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC5020_ORIGIN)
+               .addToSchema();
     }
 
     private static void addSunProprietary(final SchemaBuilder builder) {
@@ -847,10 +1064,15 @@
     }
 
     private static void addForgeRockProprietary(SchemaBuilder builder) {
-        builder.addAttributeType("1.3.6.1.4.1.36733.2.1.1.141", Collections.singletonList("fullVendorVersion"),
-                EMPTY_STRING, false, null, EMR_CASE_EXACT_IA5_OID, null, null, null,
-                SYNTAX_DIRECTORY_STRING_OID, true, false, true, AttributeUsage.DSA_OPERATION,
-                OPENDJ_ORIGIN , false);
+        builder.buildAttributeType("1.3.6.1.4.1.36733.2.1.1.141")
+               .names("fullVendorVersion")
+               .equalityMatchingRule(EMR_CASE_EXACT_IA5_OID)
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(OPENDJ_ORIGIN)
+               .addToSchema();
     }
 
     /**
@@ -972,117 +1194,208 @@
     }
 
     private static void defaultAttributeTypes(final SchemaBuilder builder) {
-        builder.addAttributeType("2.5.4.0", Collections.singletonList("objectClass"), EMPTY_STRING,
-                false, null, EMR_OID_NAME, null, null, null, SYNTAX_OID_OID, false, false, false,
-                AttributeUsage.USER_APPLICATIONS, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.0")
+               .names("objectClass")
+               .equalityMatchingRule(EMR_OID_NAME)
+               .syntax(SYNTAX_OID_OID)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.4.1", Collections.singletonList("aliasedObjectName"),
-                EMPTY_STRING, false, null, EMR_DN_NAME, null, null, null, SYNTAX_DN_OID, true,
-                false, false, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.4.1")
+               .names("aliasedObjectName")
+               .equalityMatchingRule(EMR_DN_NAME)
+               .syntax(SYNTAX_DN_OID)
+               .singleValue(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.18.1", Collections.singletonList("createTimestamp"),
-                EMPTY_STRING, false, null, EMR_GENERALIZED_TIME_NAME, OMR_GENERALIZED_TIME_NAME,
-                null, null, SYNTAX_GENERALIZED_TIME_OID, true, false, true,
-                AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.18.1")
+               .names("createTimestamp")
+               .equalityMatchingRule(EMR_GENERALIZED_TIME_NAME)
+               .orderingMatchingRule(OMR_GENERALIZED_TIME_NAME)
+               .syntax(SYNTAX_GENERALIZED_TIME_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.18.2", Collections.singletonList("modifyTimestamp"),
-                EMPTY_STRING, false, null, EMR_GENERALIZED_TIME_NAME, OMR_GENERALIZED_TIME_NAME,
-                null, null, SYNTAX_GENERALIZED_TIME_OID, true, false, true,
-                AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.18.2")
+               .names("modifyTimestamp")
+               .equalityMatchingRule(EMR_GENERALIZED_TIME_NAME)
+               .orderingMatchingRule(OMR_GENERALIZED_TIME_NAME)
+               .syntax(SYNTAX_GENERALIZED_TIME_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.18.3", Collections.singletonList("creatorsName"),
-                EMPTY_STRING, false, null, EMR_DN_NAME, null, null, null, SYNTAX_DN_OID, true,
-                false, true, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.18.3")
+               .names("creatorsName")
+               .equalityMatchingRule(EMR_DN_NAME)
+               .syntax(SYNTAX_DN_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.18.4", Collections.singletonList("modifiersName"),
-                EMPTY_STRING, false, null, EMR_DN_NAME, null, null, null, SYNTAX_DN_OID, true,
-                false, true, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.18.4")
+               .names("modifiersName")
+               .equalityMatchingRule(EMR_DN_NAME)
+               .syntax(SYNTAX_DN_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.18.10", Collections.singletonList("subschemaSubentry"),
-                EMPTY_STRING, false, null, EMR_DN_NAME, null, null, null, SYNTAX_DN_OID, true,
-                false, true, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.18.10")
+               .names("subschemaSubentry")
+               .equalityMatchingRule(EMR_DN_NAME)
+               .syntax(SYNTAX_DN_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.5", Collections.singletonList("attributeTypes"),
-                EMPTY_STRING, false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_ATTRIBUTE_TYPE_OID, false, false, false, AttributeUsage.DIRECTORY_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.5")
+               .names("attributeTypes")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_ATTRIBUTE_TYPE_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.6", Collections.singletonList("objectClasses"),
-                EMPTY_STRING, false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_OBJECTCLASS_OID, false, false, false, AttributeUsage.DIRECTORY_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.6")
+               .names("objectClasses")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_OBJECTCLASS_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.4", Collections.singletonList("matchingRules"),
-                EMPTY_STRING, false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_MATCHING_RULE_OID, false, false, false, AttributeUsage.DIRECTORY_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.4")
+               .names("matchingRules")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_MATCHING_RULE_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.8", Collections.singletonList("matchingRuleUse"),
-                EMPTY_STRING, false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_MATCHING_RULE_USE_OID, false, false, false,
-                AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.8")
+               .names("matchingRuleUse")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_MATCHING_RULE_USE_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.9", Collections.singletonList("structuralObjectClass"),
-                EMPTY_STRING, false, null, EMR_OID_NAME, null, null, null, SYNTAX_OID_OID, true,
-                false, true, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.9")
+               .names("structuralObjectClass")
+               .equalityMatchingRule(EMR_OID_NAME)
+               .syntax(SYNTAX_OID_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.10", Collections.singletonList("governingStructureRule"),
-                EMPTY_STRING, false, null, EMR_INTEGER_NAME, null, null, null, SYNTAX_INTEGER_OID,
-                true, false, true, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.10")
+               .names("governingStructureRule")
+               .equalityMatchingRule(EMR_INTEGER_NAME)
+               .syntax(SYNTAX_INTEGER_OID)
+               .singleValue(true)
+               .noUserModification(true)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.5", Collections
-                .singletonList("namingContexts"), EMPTY_STRING, false, null, null, null, null,
-                null, SYNTAX_DN_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.5")
+               .names("namingContexts")
+               .syntax(SYNTAX_DN_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.6", Collections
-                .singletonList("altServer"), EMPTY_STRING, false, null, null, null, null, null,
-                SYNTAX_IA5_STRING_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.6")
+               .names("altServer")
+               .syntax(SYNTAX_IA5_STRING_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.7", Collections
-                .singletonList("supportedExtension"), EMPTY_STRING, false, null, null, null, null,
-                null, SYNTAX_OID_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.7")
+               .names("supportedExtension")
+               .syntax(SYNTAX_OID_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.13", Collections
-                .singletonList("supportedControl"), EMPTY_STRING, false, null, null, null, null,
-                null, SYNTAX_OID_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.13")
+               .names("supportedControl")
+               .syntax(SYNTAX_OID_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.14", Collections
-                .singletonList("supportedSASLMechanisms"), EMPTY_STRING, false, null, null, null,
-                null, null, SYNTAX_DIRECTORY_STRING_OID, false, false, false,
-                AttributeUsage.DSA_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.14")
+               .names("supportedSASLMechanisms")
+               .syntax(SYNTAX_DIRECTORY_STRING_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.4203.1.3.5", Collections
-                .singletonList("supportedFeatures"), EMPTY_STRING, false, null, EMR_OID_NAME, null,
-                null, null, SYNTAX_OID_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.4203.1.3.5")
+               .names("supportedFeatures")
+               .equalityMatchingRule(EMR_OID_NAME)
+               .syntax(SYNTAX_OID_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.15", Collections
-                .singletonList("supportedLDAPVersion"), EMPTY_STRING, false, null, null, null,
-                null, null, SYNTAX_INTEGER_OID, false, false, false, AttributeUsage.DSA_OPERATION,
-                RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.15")
+               .names("supportedLDAPVersion")
+               .syntax(SYNTAX_INTEGER_OID)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("1.3.6.1.4.1.1466.101.120.16", Collections
-                .singletonList("ldapSyntaxes"), EMPTY_STRING, false, null,
-                EMR_OID_FIRST_COMPONENT_NAME, null, null, null, SYNTAX_LDAP_SYNTAX_OID, false,
-                false, false, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("1.3.6.1.4.1.1466.101.120.16")
+               .names("ldapSyntaxes")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_LDAP_SYNTAX_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.1", Collections.singletonList("ditStructureRules"),
-                EMPTY_STRING, false, null, EMR_INTEGER_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_DIT_STRUCTURE_RULE_OID, false, false, false,
-                AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.1")
+               .names("ditStructureRules")
+               .equalityMatchingRule(EMR_INTEGER_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_DIT_STRUCTURE_RULE_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.7", Collections.singletonList("nameForms"), EMPTY_STRING,
-                false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null, SYNTAX_NAME_FORM_OID,
-                false, false, false, AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.7")
+               .names("nameForms")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_NAME_FORM_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
 
-        builder.addAttributeType("2.5.21.2", Collections.singletonList("ditContentRules"),
-                EMPTY_STRING, false, null, EMR_OID_FIRST_COMPONENT_NAME, null, null, null,
-                SYNTAX_DIT_CONTENT_RULE_OID, false, false, false,
-                AttributeUsage.DIRECTORY_OPERATION, RFC4512_ORIGIN, false);
+        builder.buildAttributeType("2.5.21.2")
+               .names("ditContentRules")
+               .equalityMatchingRule(EMR_OID_FIRST_COMPONENT_NAME)
+               .syntax(SYNTAX_DIT_CONTENT_RULE_OID)
+               .usage(AttributeUsage.DIRECTORY_OPERATION)
+               .extraProperties(RFC4512_ORIGIN)
+               .addToSchema();
     }
 
     private static void defaultMatchingRules(final SchemaBuilder builder) {
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 d9653ca..b2bfefe 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
@@ -22,11 +22,24 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2015 ForgeRock AS
  *      Portions Copyright 2014 Manuel Gaupp
+ *      Portions Copyright 2011-2015 ForgeRock AS
  */
 package org.forgerock.opendj.ldap.schema;
 
+import static java.util.Collections.*;
+
+import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.schema.ObjectClass.*;
+import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
+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.*;
+
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -66,19 +79,6 @@
 import com.forgerock.opendj.util.StaticUtils;
 import com.forgerock.opendj.util.SubstringReader;
 
-import static java.util.Collections.*;
-
-import static org.forgerock.opendj.ldap.LdapException.*;
-import static org.forgerock.opendj.ldap.schema.ObjectClass.*;
-import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
-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.*;
-
 /**
  * Schema builders should be used for incremental construction of new schemas.
  */
@@ -268,22 +268,10 @@
 
             // The next set of characters must be the OID.
             final String oid = readOID(reader, allowsMalformedNamesAndOptions());
-
-            List<String> names = Collections.emptyList();
-            String description = "".intern();
-            boolean isObsolete = false;
+            AttributeType.Builder atBuilder = new AttributeType.Builder(oid, this);
+            atBuilder.definition(definition);
             String superiorType = null;
-            String equalityMatchingRule = null;
-            String orderingMatchingRule = null;
-            String substringMatchingRule = null;
-            String approximateMatchingRule = null;
             String syntax = null;
-            boolean isSingleValue = false;
-            boolean isCollective = false;
-            boolean isNoUserModification = false;
-            AttributeUsage attributeUsage = AttributeUsage.USER_APPLICATIONS;
-            Map<String, List<String>> extraProperties = Collections.emptyMap();
-
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
             // optional and it would be pretty easy to put something in the
@@ -299,17 +287,16 @@
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = readNameDescriptors(reader, allowsMalformedNamesAndOptions());
+                    atBuilder.names(readNameDescriptors(reader, allowsMalformedNamesAndOptions()));
                 } else if ("desc".equalsIgnoreCase(tokenName)) {
                     // This specifies the description for the attribute type. It
                     // is an arbitrary string of characters enclosed in single
                     // quotes.
-                    description = readQuotedString(reader);
+                    atBuilder.description(readQuotedString(reader));
                 } else if ("obsolete".equalsIgnoreCase(tokenName)) {
                     // This indicates whether the attribute type should be
-                    // considered obsolete. We do not need to do any more
-                    // parsing for this token.
-                    isObsolete = true;
+                    // considered obsolete.
+                    atBuilder.obsolete(true);
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the superior attribute
                     // type from which this attribute type should inherit its
@@ -318,15 +305,15 @@
                 } else if ("equality".equalsIgnoreCase(tokenName)) {
                     // This specifies the name or OID of the equality matching
                     // rule to use for this attribute type.
-                    equalityMatchingRule = readOID(reader, allowsMalformedNamesAndOptions());
+                    atBuilder.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 = readOID(reader, allowsMalformedNamesAndOptions());
+                    atBuilder.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 = readOID(reader, allowsMalformedNamesAndOptions());
+                    atBuilder.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,27 +324,18 @@
                     // does not impose any practical limit on the length of attribute
                     // values.
                     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
-                    // parsing for this token.
-                    isSingleValue = true;
                 } else if ("single-value".equalsIgnoreCase(tokenName)) {
                     // This indicates that attributes of this type are allowed
-                    // to have at most one value. We do not need any more parsing
-                    // for this token.
-                    isSingleValue = true;
+                    // to have at most one value.
+                    atBuilder.singleValue(true);
                 } else if ("collective".equalsIgnoreCase(tokenName)) {
-                    // This indicates that attributes of this type are
-                    // collective
-                    // (i.e., have their values generated dynamically in some
-                    // way). We do not need any more parsing for this token.
-                    isCollective = true;
+                    // This indicates that attributes of this type are collective
+                    // (i.e., have their values generated dynamically in some way).
+                    atBuilder.collective(true);
                 } else if ("no-user-modification".equalsIgnoreCase(tokenName)) {
                     // This indicates that the values of attributes of this type
-                    // are not to be modified by end users. We do not need any
-                    // more parsing for this token.
-                    isNoUserModification = true;
+                    // are not to be modified by end users.
+                    atBuilder.noUserModification(true);
                 } else if ("usage".equalsIgnoreCase(tokenName)) {
                     // This specifies the usage string for this attribute type.
                     // It should be followed by one of the strings
@@ -371,17 +349,16 @@
                     while (reader.read() != ' ') {
                         length++;
                     }
-
                     reader.reset();
                     final String usageStr = reader.read(length);
                     if ("userapplications".equalsIgnoreCase(usageStr)) {
-                        attributeUsage = AttributeUsage.USER_APPLICATIONS;
+                        atBuilder.usage(AttributeUsage.USER_APPLICATIONS);
                     } else if ("directoryoperation".equalsIgnoreCase(usageStr)) {
-                        attributeUsage = AttributeUsage.DIRECTORY_OPERATION;
+                        atBuilder.usage(AttributeUsage.DIRECTORY_OPERATION);
                     } else if ("distributedoperation".equalsIgnoreCase(usageStr)) {
-                        attributeUsage = AttributeUsage.DISTRIBUTED_OPERATION;
+                        atBuilder.usage(AttributeUsage.DISTRIBUTED_OPERATION);
                     } else if ("dsaoperation".equalsIgnoreCase(usageStr)) {
-                        attributeUsage = AttributeUsage.DSA_OPERATION;
+                        atBuilder.usage(AttributeUsage.DSA_OPERATION);
                     } else {
                         throw new LocalizedIllegalArgumentException(
                             WARN_ATTR_SYNTAX_ATTRTYPE_INVALID_ATTRIBUTE_USAGE1.get(definition, usageStr));
@@ -392,23 +369,17 @@
                     // or an open parenthesis followed by one or more values in
                     // single quotes separated by spaces followed by a close
                     // parenthesis.
-                    if (extraProperties.isEmpty()) {
-                        extraProperties = new HashMap<String, List<String>>();
-                    }
-                    extraProperties.put(tokenName, SchemaUtils.readExtensions(reader));
+                    List<String> extensions = readExtensions(reader);
+                    atBuilder.extraProperties(tokenName, extensions);
                 } else {
                     throw new LocalizedIllegalArgumentException(
                         ERR_ATTR_SYNTAX_ATTRTYPE_ILLEGAL_TOKEN1.get(definition, tokenName));
                 }
             }
 
-            final List<String> approxRules = extraProperties.get(SCHEMA_PROPERTY_APPROX_RULE);
+            final List<String> approxRules = atBuilder.getExtraProperties().get(SCHEMA_PROPERTY_APPROX_RULE);
             if (approxRules != null && !approxRules.isEmpty()) {
-                approximateMatchingRule = approxRules.get(0);
-            }
-
-            if (!extraProperties.isEmpty()) {
-                extraProperties = Collections.unmodifiableMap(extraProperties);
+                atBuilder.approximateMatchingRule(approxRules.get(0));
             }
 
             if (superiorType == null && syntax == null) {
@@ -416,96 +387,14 @@
                     WARN_ATTR_SYNTAX_ATTRTYPE_MISSING_SYNTAX_AND_SUPERIOR.get(definition));
             }
 
-            final AttributeType attrType =
-                    new AttributeType(oid, names, description, isObsolete, superiorType,
-                            equalityMatchingRule, orderingMatchingRule, substringMatchingRule,
-                            approximateMatchingRule, syntax, isSingleValue, isCollective,
-                            isNoUserModification, attributeUsage, extraProperties, definition);
+            atBuilder.superiorType(superiorType)
+                     .syntax(syntax);
 
-            addAttributeType(attrType, overwrite);
+            return overwrite ? atBuilder.addToSchemaOverwrite() : atBuilder.addToSchema();
         } catch (final DecodeException e) {
-            final LocalizableMessage msg =
-                    ERR_ATTR_SYNTAX_ATTRTYPE_INVALID1.get(definition, e.getMessageObject());
+            final LocalizableMessage msg = ERR_ATTR_SYNTAX_ATTRTYPE_INVALID1.get(definition, e.getMessageObject());
             throw new LocalizedIllegalArgumentException(msg, e.getCause());
         }
-        return this;
-    }
-
-    /**
-     * Adds the provided attribute type definition to this schema builder.
-     *
-     * @param oid
-     *            The OID of the attribute type definition.
-     * @param names
-     *            The user-friendly names of the attribute type definition.
-     * @param description
-     *            The description of the attribute type definition.
-     * @param obsolete
-     *            {@code true} if the attribute type definition is obsolete,
-     *            otherwise {@code false}.
-     * @param superiorType
-     *            The OID of the superior attribute type definition.
-     * @param equalityMatchingRule
-     *            The OID of the equality matching rule, which may be
-     *            {@code null} indicating that the superior attribute type's
-     *            matching rule should be used or, if none is defined, the
-     *            default matching rule associated with the syntax.
-     * @param orderingMatchingRule
-     *            The OID of the ordering matching rule, which may be
-     *            {@code null} indicating that the superior attribute type's
-     *            matching rule should be used or, if none is defined, the
-     *            default matching rule associated with the syntax.
-     * @param substringMatchingRule
-     *            The OID of the substring matching rule, which may be
-     *            {@code null} indicating that the superior attribute type's
-     *            matching rule should be used or, if none is defined, the
-     *            default matching rule associated with the syntax.
-     * @param approximateMatchingRule
-     *            The OID of the approximate matching rule, which may be
-     *            {@code null} indicating that the superior attribute type's
-     *            matching rule should be used or, if none is defined, the
-     *            default matching rule associated with the syntax.
-     * @param syntax
-     *            The OID of the syntax definition.
-     * @param singleValue
-     *            {@code true} if the attribute type definition is
-     *            single-valued, otherwise {@code false}.
-     * @param collective
-     *            {@code true} if the attribute type definition is a collective
-     *            attribute, otherwise {@code false}.
-     * @param noUserModification
-     *            {@code true} if the attribute type definition is read-only,
-     *            otherwise {@code false}.
-     * @param attributeUsage
-     *            The intended use of the attribute type definition.
-     * @param extraProperties
-     *            A map containing additional properties associated with the
-     *            attribute type definition.
-     * @param overwrite
-     *            {@code true} if any existing attribute type with the same OID
-     *            should be overwritten.
-     * @return A reference to this schema builder.
-     * @throws ConflictingSchemaElementException
-     *             If {@code overwrite} was {@code false} and a conflicting
-     *             schema element was found.
-     */
-    SchemaBuilder addAttributeType(final String oid, final List<String> names,
-            final String description, final boolean obsolete, final String superiorType,
-            final String equalityMatchingRule, final String orderingMatchingRule,
-            final String substringMatchingRule, final String approximateMatchingRule,
-            final String syntax, final boolean singleValue, final boolean collective,
-            final boolean noUserModification, final AttributeUsage attributeUsage,
-            final Map<String, List<String>> extraProperties, final boolean overwrite) {
-        lazyInitBuilder();
-
-        final AttributeType attrType =
-                new AttributeType(oid, unmodifiableCopyOfList(names), description, obsolete,
-                        superiorType, equalityMatchingRule, orderingMatchingRule,
-                        substringMatchingRule, approximateMatchingRule, syntax, singleValue,
-                        collective, noUserModification, attributeUsage,
-                        unmodifiableCopyOfExtraProperties(extraProperties), null);
-        addAttributeType(attrType, overwrite);
-        return this;
     }
 
     /**
@@ -1138,6 +1027,39 @@
     }
 
     /**
+     * Returns an attribute type builder whose fields are initialized to the
+     * values of the provided attribute type. This method should be used when
+     * duplicating attribute types from external schemas or when modifying
+     * existing attribute types.
+     *
+     * @param attributeType
+     *            The attribute type source.
+     * @return A builder to continue building the AttributeType.
+     */
+    public AttributeType.Builder buildAttributeType(final AttributeType attributeType) {
+        lazyInitBuilder();
+        return new AttributeType.Builder(attributeType, this);
+    }
+
+    /**
+     * Returns a builder which can be used for incrementally constructing a new
+     * attribute type before adding it to the schema. Example usage:
+     *
+     * <pre>
+     * SchemaBuilder builder = ...;
+     * builder.buildAttributeType("attributetype-oid").name("attribute type name").addToSchema();
+     * </pre>
+     *
+     * @param oid
+     *            The OID of the attribute type definition.
+     * @return A builder to continue building the AttributeType.
+     */
+    public AttributeType.Builder buildAttributeType(final String oid) {
+        lazyInitBuilder();
+        return new AttributeType.Builder(oid, this);
+    }
+
+    /**
      * Returns a builder which can be used for incrementally constructing a new matching rule before adding it to the
      * schema. Example usage:
      *
@@ -2321,7 +2243,7 @@
         return schema;
     }
 
-    private void addAttributeType(final AttributeType attribute, final boolean overwrite) {
+    SchemaBuilder addAttributeType(final AttributeType attribute, final boolean overwrite) {
         AttributeType conflictingAttribute;
         if (numericOID2AttributeTypes.containsKey(attribute.getOID())) {
             conflictingAttribute = numericOID2AttributeTypes.get(attribute.getOID());
@@ -2348,6 +2270,8 @@
                 attrs.add(attribute);
             }
         }
+
+        return this;
     }
 
     private void addDITContentRule(final DITContentRule rule, final boolean overwrite) {
@@ -2556,7 +2480,7 @@
         }
 
         for (final AttributeType attributeType : schema.getAttributeTypes()) {
-            addAttributeType(attributeType.duplicate(), overwrite);
+            addAttributeType(attributeType, overwrite);
         }
 
         for (final ObjectClass objectClass : schema.getObjectClasses()) {
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
index 23c6729..91a50e7 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaElement.java
@@ -102,8 +102,20 @@
          *            The optional list of values for the extended property.
          * @return This builder.
          */
-        public abstract T extraProperties(final String extensionName,
-                final String... extensionValues);
+        public abstract T extraProperties(final String extensionName, final String... extensionValues);
+
+        /**
+         * Adds the provided extended property.
+         *
+         * @param extensionName
+         *            The name of the extended property.
+         * @param extensionValues
+         *            The optional list of values for the extended property.
+         * @return This builder.
+         */
+        public T extraProperties(final String extensionName, final List<String> extensionValues) {
+            return extraProperties(extensionName, extensionValues.toArray(new String[extensionValues.size()]));
+        }
 
         /**
          * Removes all extra properties.
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeBuilderTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeBuilderTestCase.java
new file mode 100644
index 0000000..93d6ad3
--- /dev/null
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeBuilderTestCase.java
@@ -0,0 +1,186 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License, Version 1.0 only
+ * (the "License").  You may not use this file except in compliance
+ * with the License.
+ *
+ * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
+ * or http://forgerock.org/license/CDDLv1.0.html.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at legal-notices/CDDLv1_0.txt.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information:
+ *      Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ *
+ *
+ *      Copyright 2015 ForgeRock AS.
+ */
+package org.forgerock.opendj.ldap.schema;
+
+import static java.util.Collections.*;
+
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.MapAssert.*;
+import static org.forgerock.opendj.ldap.schema.Schema.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+
+import java.util.List;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+public class AttributeTypeBuilderTestCase extends AbstractSchemaTestCase {
+
+    @DataProvider
+    Object[][] validAttributeTypes() {
+        // OID, names, description, obsolete, superior type, equalityMR,
+        // orderingMR, substringMR, approximateMR, syntax, singleValue,
+        // collective, noUserModification, attributeUsage, extraPropertyName,
+        // extraPropertyValue
+        return new Object[][] {
+            // Basic attribute type
+            { "1.2.3.4", singletonList("MyAttributeType"), "MyAttributeType description.", false, "2.5.4.0",
+                EMR_CERTIFICATE_EXACT_OID, OMR_UUID_OID, SMR_CASE_IGNORE_LIST_OID, AMR_DOUBLE_METAPHONE_OID, null,
+                false, false, false, AttributeUsage.USER_APPLICATIONS, "New extra property", "New extra value", false },
+            // Allowed overrides existing core schema attribute type name
+            { "2.5.4.41", singletonList("name"), "MyAttributeType description.", false, null,
+                EMR_CERTIFICATE_EXACT_OID, null, SMR_CASE_IGNORE_LIST_OID, AMR_DOUBLE_METAPHONE_OID,
+                SYNTAX_DIRECTORY_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
+                "New extra property", "New extra value", true },
+            // No name provided, should be validated
+            { "1.2.3.4", emptyList(), "MyAttributeType description.", false, null, EMR_CASE_IGNORE_LIST_OID,
+                OMR_CASE_IGNORE_OID, null, null, SYNTAX_DIRECTORY_STRING_OID, false, false, false,
+                AttributeUsage.USER_APPLICATIONS, "New extra property", "New extra value", false },
+            // Empty description, should be validated
+            { "1.2.3.4", singletonList("MyAttributeType"), "", false, null, EMR_CASE_IGNORE_LIST_OID, null, null,
+                null, SYNTAX_DIRECTORY_STRING_OID, false, false, true, AttributeUsage.DIRECTORY_OPERATION,
+                "New extra property", "New extra value", false },
+        };
+    }
+
+    @Test(dataProvider = "validAttributeTypes")
+    public void testValidAttributeTypeBuilder(final String oid, final List<String> names, final String description,
+            final boolean obsolete, final String superiorType, final String equalityMatchingRule,
+            final String orderingMatchingRule, final String substringMatchingRule,
+            final String approximateMatchingRule, final String syntax, final boolean singleValue,
+            final boolean collective, final boolean noUserModification, final AttributeUsage attributeUsage,
+            final String extraPropertyName, String extraPropertyValue, final boolean overwrite) throws Exception {
+        AttributeType.Builder atBuilder = new SchemaBuilder(getCoreSchema())
+            .buildAttributeType(oid)
+            .names(names)
+            .description(description)
+            .obsolete(obsolete)
+            .superiorType(superiorType)
+            .equalityMatchingRule(equalityMatchingRule)
+            .orderingMatchingRule(orderingMatchingRule)
+            .substringMatchingRule(substringMatchingRule)
+            .approximateMatchingRule(approximateMatchingRule)
+            .syntax(syntax)
+            .singleValue(singleValue)
+            .collective(collective)
+            .noUserModification(noUserModification)
+            .usage(attributeUsage)
+            .extraProperties(extraPropertyName, extraPropertyValue);
+
+        Schema schema = overwrite ? atBuilder.addToSchemaOverwrite().toSchema()
+                                  : atBuilder.addToSchema().toSchema();
+
+        assertThat(schema.getWarnings()).isEmpty();
+        final AttributeType at = schema.getAttributeType(oid);
+        assertThat(at).isNotNull();
+        assertThat(at.getOID()).isEqualTo(oid);
+        assertThat(at.getNames()).containsOnly(names.toArray());
+        assertThat(at.getDescription()).isEqualTo(description);
+        assertThat(at.isObsolete()).isEqualTo(obsolete);
+        assertThat(at.getExtraProperties()).includes(entry(extraPropertyName, singletonList(extraPropertyValue)));
+    }
+
+    @Test
+    public void testAttributeTypeBuilderDefaultValues() throws Exception {
+        final String testOID = "1.1.1.42";
+        final SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
+        AttributeType.Builder ocBuilder = sb.buildAttributeType(testOID)
+                                            .names("defaultAttributeType")
+                                            .syntax(SYNTAX_OID_OID)
+                                            .usage(AttributeUsage.USER_APPLICATIONS);
+
+        Schema schema = ocBuilder.addToSchema().toSchema();
+        assertThat(schema.getWarnings()).isEmpty();
+
+        AttributeType at = schema.getAttributeType(testOID);
+        assertThat(at).isNotNull();
+        assertThat(at.getOID()).isEqualTo(testOID);
+        assertThat(at.getNames()).containsOnly("defaultAttributeType");
+        assertThat(at.getDescription()).isEqualTo("");
+        assertThat(at.isObsolete()).isFalse();
+        assertThat(at.getSuperiorType()).isNull();
+        assertThat(at.getSyntax().getOID()).isEqualTo(SYNTAX_OID_OID);
+        assertThat(at.isSingleValue()).isFalse();
+        assertThat(at.isCollective()).isFalse();
+        assertThat(at.isNoUserModification()).isFalse();
+        assertThat(at.getExtraProperties()).isEmpty();
+    }
+
+    @Test
+    public void testAttributeTypeBuilderCopyConstructor() throws Exception {
+        SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
+        AttributeType.Builder atBuilder = sb.buildAttributeType("1.1.1.42")
+                                            .names("AttributeTypeToDuplicate")
+                                            .description("Attribute type to duplicate")
+                                            .usage(AttributeUsage.USER_APPLICATIONS)
+                                            .syntax(SYNTAX_OID_OID);
+        Schema schema = atBuilder.addToSchema().toSchema();
+        assertThat(schema.getWarnings()).isEmpty();
+
+        sb.buildAttributeType(schema.getAttributeType("AttributeTypeToDuplicate"))
+                .oid("1.1.1.43")
+                .names("Copy")
+                .obsolete(true)
+                .addToSchemaOverwrite();
+        Schema schemaCopy = sb.toSchema();
+        assertThat(schemaCopy.getWarnings()).isEmpty();
+
+        AttributeType atCopy = schemaCopy.getAttributeType("Copy");
+        assertThat(atCopy).isNotNull();
+        assertThat(atCopy.getOID()).isEqualTo("1.1.1.43");
+        assertThat(atCopy.getDescription()).isEqualTo("Attribute type to duplicate");
+        assertThat(atCopy.isObsolete()).isTrue();
+        assertThat(atCopy.getNames()).containsOnly("AttributeTypeToDuplicate", "Copy");
+        assertThat(atCopy.getExtraProperties()).isEmpty();
+    }
+
+    @Test(expectedExceptions = ConflictingSchemaElementException.class)
+    public void testAttributeTypeBuilderDoesNotAllowOverwrite() throws Exception {
+        AttributeType.Builder atBuilder = new SchemaBuilder(getCoreSchema())
+            .buildAttributeType("2.5.4.25")
+            .description("MyAttributeType description")
+            .names("internationalISDNNumber")
+            .syntax(SYNTAX_OID_OID)
+            .usage(AttributeUsage.DSA_OPERATION)
+            .extraProperties("New extra property", "New extra value");
+
+        atBuilder.addToSchema().toSchema();
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testAttributeTypeBuilderDoesNotAllowEmptyOID() throws Exception {
+        AttributeType.Builder atBuilder = new SchemaBuilder(getCoreSchema())
+            .buildAttributeType("")
+            .description("MyAttributeType description")
+            .names("MyAttributeType")
+            .syntax(SYNTAX_OID_DESCRIPTION)
+            .usage(AttributeUsage.DIRECTORY_OPERATION)
+            .extraProperties("New extra property", "New extra value");
+
+        atBuilder.addToSchema().toSchema();
+    }
+
+}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java
index 0b3f9cf..bed5db5 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/AttributeTypeTest.java
@@ -22,14 +22,13 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Copyright 2015 ForgeRock AS.
  */
 package org.forgerock.opendj.ldap.schema;
 
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
 
-import java.util.Collections;
 import java.util.Iterator;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 
@@ -48,28 +47,43 @@
 
     public AttributeTypeTest() throws Exception {
         final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
-        builder.addAttributeType("1.2.1", EMPTY_NAMES, "", true, null, null, null, null, null,
-                "1.3.6.1.4.1.1466.115.121.1.27", true, false, false,
-                AttributeUsage.USER_APPLICATIONS, EMPTY_PROPS, false);
+        builder.buildAttributeType("1.2.1")
+               .names(EMPTY_NAMES)
+               .obsolete(true)
+               .syntax("1.3.6.1.4.1.1466.115.121.1.27")
+               .singleValue(true)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .addToSchema();
+
         builder.addAttributeType(
                 "( 1.2.2 OBSOLETE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE "
                         + " COLLECTIVE X-ORIGIN ( 'Sun Java System Identity Management' "
                         + "'user defined' ) X-SCHEMA-FILE '98sunEmp.ldif')", false);
-        builder.addAttributeType("1.2.3", Collections.singletonList("testType"), "", false,
-                "1.2.2", null, null, null, null, "1.3.6.1.4.1.1466.115.121.1.27", false, true,
-                false, AttributeUsage.USER_APPLICATIONS, EMPTY_PROPS, false);
-        builder.addAttributeType("( 1.2.4 NAME 'testType' SUP 1.2.3 SINGLE-VALUE COLLECTIVE )",
-                false);
-        final List<String> names = new LinkedList<String>();
-        names.add("testType");
-        names.add("testnamealias");
-        names.add("anothernamealias");
-        builder.addAttributeType("1.2.5", names, "", false, null, EMR_CASE_IGNORE_LIST_OID, null,
-                SMR_CASE_IGNORE_LIST_OID, AMR_DOUBLE_METAPHONE_OID, SYNTAX_INTEGER_OID, false,
-                false, true, AttributeUsage.DSA_OPERATION, EMPTY_PROPS, false);
+
+        builder.buildAttributeType("1.2.3")
+               .names("testType")
+               .superiorType("1.2.2")
+               .syntax("1.3.6.1.4.1.1466.115.121.1.27")
+               .collective(true)
+               .usage(AttributeUsage.USER_APPLICATIONS)
+               .addToSchema();
+
+        builder.addAttributeType("( 1.2.4 NAME 'testType' SUP 1.2.3 SINGLE-VALUE COLLECTIVE )", false);
+
+        builder.buildAttributeType("1.2.5")
+               .names("testType", "testnamealias", "anothernamealias")
+               .equalityMatchingRule(EMR_CASE_IGNORE_LIST_OID)
+               .substringMatchingRule(SMR_CASE_IGNORE_LIST_OID)
+               .approximateMatchingRule(AMR_DOUBLE_METAPHONE_OID)
+               .syntax(SYNTAX_INTEGER_OID)
+               .noUserModification(true)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .addToSchema();
+
         builder.addAttributeType("( 1.2.6 NAME ( 'testType' 'testnamealias' 'anothernamealias1' ) "
                 + " SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SUP anothernamealias"
                 + " USAGE dSAOperation NO-USER-MODIFICATION )", false);
+
         schema = builder.toSchema();
         if (!schema.getWarnings().isEmpty()) {
             throw new Exception("Base schema not valid!");
@@ -486,10 +500,14 @@
     @Test(expectedExceptions = IllegalArgumentException.class)
     public void testNoSupNorSyntax1() throws Exception {
         final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
-        builder.addAttributeType("1.2.1", EMPTY_NAMES, "", true, null, null, null, null, null,
-                null, false, false, false, AttributeUsage.DSA_OPERATION, EMPTY_PROPS, false);
-        builder.addAttributeType(
-                "( 1.2.2 OBSOLETE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )", false);
+        builder.buildAttributeType("1.2.1")
+               .names(EMPTY_NAMES)
+               .obsolete(true)
+               .usage(AttributeUsage.DSA_OPERATION)
+               .addToSchema();
+
+        builder.addAttributeType("( 1.2.2 OBSOLETE SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE )", false);
+
     }
 
     /**
@@ -520,9 +538,14 @@
     protected SchemaElement getElement(final String description,
             final Map<String, List<String>> extraProperties) throws SchemaException {
         final SchemaBuilder builder = new SchemaBuilder(Schema.getCoreSchema());
-        builder.addAttributeType("1.2.3", Collections.singletonList("testType"), description,
-                false, null, null, null, null, null, "1.3.6.1.4.1.1466.115.121.1.27", false, false,
-                false, AttributeUsage.DSA_OPERATION, extraProperties, false);
+        builder.buildAttributeType("1.2.3")
+               .names("testType")
+               .description(description)
+               .syntax("1.3.6.1.4.1.1466.115.121.1.27")
+               .usage(AttributeUsage.DSA_OPERATION)
+               .extraProperties(extraProperties)
+               .addToSchema();
+
         return builder.toSchema().getAttributeType("1.2.3");
     }
 
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 fe509c9..0ea5e44 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
@@ -25,7 +25,15 @@
  */
 package org.forgerock.opendj.ldap.schema;
 
-import java.util.Collections;
+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.Schema.*;
+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.*;
 
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
@@ -41,16 +49,6 @@
 import org.forgerock.opendj.ldap.responses.SearchResultEntry;
 import org.testng.annotations.Test;
 
-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.Schema.*;
-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.*;
-
 /**
  * Test SchemaBuilder.
  */
@@ -995,12 +993,11 @@
      */
     @Test(expectedExceptions = NullPointerException.class)
     public final void testSchemaBuilderAddAttributeTypeDoesntAllowNull() throws Exception {
-
         final SchemaBuilder scBuild = new SchemaBuilder();
         // Adding the new schema containing the customclass
         scBuild.addObjectClass("( temporary-fake-oc-id NAME 'myCustomObjClass"
                 + "' SUP top AUXILIARY MAY myCustomAttribute )", false);
-        scBuild.addAttributeType(null, false);
+        scBuild.addAttributeType((String) null, false);
     }
 
     /**
@@ -1087,11 +1084,16 @@
         final SchemaBuilder scBuild2 = new SchemaBuilder(Schema.getDefaultSchema());
         scBuild2.addObjectClass("( temporary-fake-oc-id NAME 'myCustomObjClass"
                 + "' SUP top AUXILIARY MAY myCustomAttribute )", false);
-        scBuild2.addAttributeType("temporary-fake-attr-id", Collections
-                .singletonList("myCustomAttribute"), "The new attribute type", false, null,
-                "caseIgnoreOrderingMatch", "caseIgnoreOrderingMatch", "caseIgnoreSubstringsMatch",
-                null, "1.3.6.1.4.1.1466.115.121.1.15", false, false, true,
-                AttributeUsage.USER_APPLICATIONS, null, false);
+
+        scBuild2.buildAttributeType("temporary-fake-attr-id")
+                .names("myCustomAttribute")
+                .description("The new attribute type")
+                .equalityMatchingRule("caseIgnoreOrderingMatch")
+                .orderingMatchingRule("caseIgnoreOrderingMatch")
+                .substringMatchingRule("caseIgnoreSubstringsMatch")
+                .syntax("1.3.6.1.4.1.1466.115.121.1.15")
+                .usage(AttributeUsage.USER_APPLICATIONS)
+                .addToSchemaOverwrite();
         Schema sc2 = scBuild2.toSchema();
 
         assertThat(sc2.getAttributeType("myCustomAttribute").getDescription()).isEqualTo(

--
Gitblit v1.10.0