From cc1f99dc6bf00e63e6a1df0c6f39d9e37d176b04 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Wed, 14 Jan 2015 10:33:13 +0000
Subject: [PATCH] OPENDJ-1652 (CR-5591) Add fluent builder for object class

---
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java |  208 +++++++++
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java             |  502 ++++++++++------------
 opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java      |   26 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java                |  403 ++++++++++++++++--
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java              |  174 +++----
 5 files changed, 861 insertions(+), 452 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
index ba8d86a..9e3bbc3 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CoreSchemaImpl.java
@@ -22,12 +22,13 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions copyright 2013-2014 ForgeRock AS.
+ *      Portions copyright 2013-2015 ForgeRock AS.
  *      Portions copyright 2014 Manuel Gaupp
  */
 package org.forgerock.opendj.ldap.schema;
 
 import static org.forgerock.opendj.ldap.schema.CollationMatchingRulesImpl.*;
+import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
 import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
 import static org.forgerock.opendj.ldap.schema.TimeBasedMatchingRulesImpl.*;
 
@@ -35,12 +36,10 @@
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Map.Entry;
-import java.util.Set;
 import java.util.TreeMap;
 
 final class CoreSchemaImpl {
@@ -73,8 +72,6 @@
 
     private static final String EMPTY_STRING = "".intern();
 
-    private static final Set<String> EMPTY_STRING_SET = Collections.emptySet();
-
     private static final Schema SINGLETON;
 
     /**
@@ -337,10 +334,13 @@
                 .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.addObjectClass("1.3.6.1.4.1.4203.1.4.7", Collections
-                .singletonList("authPasswordObject"), "authentication password mix in class",
-                false, EMPTY_STRING_SET, EMPTY_STRING_SET, Collections.singleton("authPassword"),
-                ObjectClassType.AUXILIARY, RFC3112_ORIGIN, false);
+        builder.buildObjectClass("1.3.6.1.4.1.4203.1.4.7")
+                .names("authPasswordObject")
+                .type(AUXILIARY)
+                .description("authentication password mix in class")
+                .optionalAttributes("authPassword")
+                .extraProperties(RFC3112_ORIGIN)
+                .addToSchema();
     }
 
     private static void addRFC4519(final SchemaBuilder builder) {
@@ -540,229 +540,137 @@
                 SYNTAX_BIT_STRING_OID, false, false, false, AttributeUsage.USER_APPLICATIONS,
                 RFC4519_ORIGIN, false);
 
-        Set<String> attrs = new HashSet<String>();
-        attrs.add("seeAlso");
-        attrs.add("ou");
-        attrs.add("l");
-        attrs.add("description");
+        builder.buildObjectClass("2.5.6.11")
+                .names("applicationProcess")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("cn")
+                .optionalAttributes("seeAlso", "ou", "l", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.11", Collections.singletonList("applicationProcess"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
-                        .singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.2")
+                .names("country")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("c")
+                .optionalAttributes("searchGuide", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("searchGuide");
-        attrs.add("description");
+        builder.buildObjectClass("1.3.6.1.4.1.1466.344")
+                .names("dcObject")
+                .type(AUXILIARY)
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("dc")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.2", Collections.singletonList("country"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("c"),
-                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.14")
+                .names("device")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("cn")
+                .optionalAttributes("serialNumber", "seeAlso", "owner", "ou", "o", "l", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("1.3.6.1.4.1.1466.344", Collections.singletonList("dcObject"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
-                        .singleton("dc"), EMPTY_STRING_SET, ObjectClassType.AUXILIARY,
-                RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.9")
+                .names("groupOfNames")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("member", "cn")
+                .optionalAttributes("businessCategory", "seeAlso", "owner", "ou", "o", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("serialNumber");
-        attrs.add("seeAlso");
-        attrs.add("owner");
-        attrs.add("ou");
-        attrs.add("o");
-        attrs.add("l");
-        attrs.add("description");
+        builder.buildObjectClass("2.5.6.17")
+                .names("groupOfUniqueNames")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("member", "cn")
+                .optionalAttributes("businessCategory", "seeAlso", "owner", "ou", "o", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.14", Collections.singletonList("device"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("cn"),
-                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.3")
+                .names("locality")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .optionalAttributes("street", "seeAlso", "searchGuide", "st", "l", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        Set<String> must = new HashSet<String>();
-        must.add("member");
-        must.add("cn");
+        builder.buildObjectClass("2.5.6.4")
+                .names("organization")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("o")
+                .optionalAttributes("userPassword", "searchGuide", "seeAlso", "businessCategory", "x121Address",
+                        "registeredAddress", "destinationIndicator", "preferredDeliveryMethod", "telexNumber",
+                        "teletexTerminalIdentifier", "telephoneNumber", "internationalISDNNumber",
+                        "facsimileTelephoneNumber", "street", "postOfficeBox", "postalCode", "postalAddress",
+                        "physicalDeliveryOfficeName", "st", "l", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("businessCategory");
-        attrs.add("seeAlso");
-        attrs.add("owner");
-        attrs.add("ou");
-        attrs.add("o");
-        attrs.add("description");
+        builder.buildObjectClass("2.5.6.7")
+                .names("organizationalPerson")
+                .superiorObjectClasses("person")
+                .optionalAttributes("title", "x121Address", "registeredAddress", "destinationIndicator",
+                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
+                        "internationalISDNNumber", "facsimileTelephoneNumber", "street", "postOfficeBox",
+                        "postalCode", "postalAddress", "physicalDeliveryOfficeName", "ou", "st", "l")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.9", Collections.singletonList("groupOfNames"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
-                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.8")
+                .names("organizationalRole")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("cn")
+                .optionalAttributes("x121Address", "registeredAddress", "destinationIndicator",
+                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
+                        "internationalISDNNumber", "facsimileTelephoneNumber", "seeAlso", "roleOccupant",
+                        "preferredDeliveryMethod", "street", "postOfficeBox", "postalCode", "postalAddress",
+                        "physicalDeliveryOfficeName", "ou", "st", "l", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("businessCategory");
-        attrs.add("seeAlso");
-        attrs.add("owner");
-        attrs.add("ou");
-        attrs.add("o");
-        attrs.add("description");
+        builder.buildObjectClass("2.5.6.5")
+                .names("organizationalUnit")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("ou")
+                .optionalAttributes("businessCategory", "description", "destinationIndicator",
+                        "facsimileTelephoneNumber", "internationalISDNNumber", "l", "physicalDeliveryOfficeName",
+                        "postalAddress", "postalCode", "postOfficeBox", "preferredDeliveryMethod",
+                        "registeredAddress", "searchGuide", "seeAlso", "st", "street", "telephoneNumber",
+                        "teletexTerminalIdentifier", "telexNumber", "userPassword", "x121Address")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.17", Collections.singletonList("groupOfUniqueNames"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
-                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.6")
+                .names("person")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("sn", "cn")
+                .optionalAttributes("userPassword", "telephoneNumber", "destinationIndicator", "seeAlso", "description")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("street");
-        attrs.add("seeAlso");
-        attrs.add("searchGuide");
-        attrs.add("st");
-        attrs.add("l");
-        attrs.add("description");
+        builder.buildObjectClass("2.5.6.10")
+                .names("residentialPerson")
+                .superiorObjectClasses("person")
+                .requiredAttributes("l")
+                .optionalAttributes("businessCategory", "x121Address", "registeredAddress", "destinationIndicator",
+                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
+                        "internationalISDNNumber", "facsimileTelephoneNumber", "preferredDeliveryMethod", "street",
+                        "postOfficeBox", "postalCode", "postalAddress", "physicalDeliveryOfficeName", "st", "l")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.3", Collections.singletonList("locality"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET, attrs,
-                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        attrs = new HashSet<String>();
-        attrs.add("userPassword");
-        attrs.add("searchGuide");
-        attrs.add("seeAlso");
-        attrs.add("businessCategory");
-        attrs.add("x121Address");
-        attrs.add("registeredAddress");
-        attrs.add("destinationIndicator");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("telexNumber");
-        attrs.add("teletexTerminalIdentifier");
-        attrs.add("telephoneNumber");
-        attrs.add("internationalISDNNumber");
-        attrs.add("facsimileTelephoneNumber");
-        attrs.add("street");
-        attrs.add("postOfficeBox");
-        attrs.add("postalCode");
-        attrs.add("postalAddress");
-        attrs.add("physicalDeliveryOfficeName");
-        attrs.add("st");
-        attrs.add("l");
-        attrs.add("description");
-
-        builder.addObjectClass("2.5.6.4", Collections.singletonList("organization"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("o"),
-                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        attrs = new HashSet<String>();
-        attrs.add("title");
-        attrs.add("x121Address");
-        attrs.add("registeredAddress");
-        attrs.add("destinationIndicator");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("telexNumber");
-        attrs.add("teletexTerminalIdentifier");
-        attrs.add("telephoneNumber");
-        attrs.add("internationalISDNNumber");
-        attrs.add("facsimileTelephoneNumber");
-        attrs.add("street");
-        attrs.add("postOfficeBox");
-        attrs.add("postalCode");
-        attrs.add("postalAddress");
-        attrs.add("physicalDeliveryOfficeName");
-        attrs.add("ou");
-        attrs.add("st");
-        attrs.add("l");
-
-        builder.addObjectClass("2.5.6.7", Collections.singletonList("organizationalPerson"),
-                EMPTY_STRING, false, Collections.singleton("person"), EMPTY_STRING_SET, attrs,
-                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        attrs = new HashSet<String>();
-        attrs.add("x121Address");
-        attrs.add("registeredAddress");
-        attrs.add("destinationIndicator");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("telexNumber");
-        attrs.add("teletexTerminalIdentifier");
-        attrs.add("telephoneNumber");
-        attrs.add("internationalISDNNumber");
-        attrs.add("facsimileTelephoneNumber");
-        attrs.add("seeAlso");
-        attrs.add("roleOccupant");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("street");
-        attrs.add("postOfficeBox");
-        attrs.add("postalCode");
-        attrs.add("postalAddress");
-        attrs.add("physicalDeliveryOfficeName");
-        attrs.add("ou");
-        attrs.add("st");
-        attrs.add("l");
-        attrs.add("description");
-
-        builder.addObjectClass("2.5.6.8", Collections.singletonList("organizationalRole"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
-                        .singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        attrs = new HashSet<String>();
-        attrs.add("businessCategory");
-        attrs.add("description");
-        attrs.add("destinationIndicator");
-        attrs.add("facsimileTelephoneNumber");
-        attrs.add("internationalISDNNumber");
-        attrs.add("l");
-        attrs.add("physicalDeliveryOfficeName");
-        attrs.add("postalAddress");
-        attrs.add("postalCode");
-        attrs.add("postOfficeBox");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("registeredAddress");
-        attrs.add("searchGuide");
-        attrs.add("seeAlso");
-        attrs.add("st");
-        attrs.add("street");
-        attrs.add("telephoneNumber");
-        attrs.add("teletexTerminalIdentifier");
-        attrs.add("telexNumber");
-        attrs.add("userPassword");
-        attrs.add("x121Address");
-
-        builder.addObjectClass("2.5.6.5", Collections.singletonList("organizationalUnit"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
-                        .singleton("ou"), attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        must = new HashSet<String>();
-        must.add("sn");
-        must.add("cn");
-
-        attrs = new HashSet<String>();
-        attrs.add("userPassword");
-        attrs.add("telephoneNumber");
-        attrs.add("destinationIndicator");
-        attrs.add("seeAlso");
-        attrs.add("description");
-
-        builder.addObjectClass("2.5.6.6", Collections.singletonList("person"), EMPTY_STRING, false,
-                Collections.singleton(TOP_OBJECTCLASS_NAME), must, attrs,
-                ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        attrs = new HashSet<String>();
-        attrs.add("businessCategory");
-        attrs.add("x121Address");
-        attrs.add("registeredAddress");
-        attrs.add("destinationIndicator");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("telexNumber");
-        attrs.add("teletexTerminalIdentifier");
-        attrs.add("telephoneNumber");
-        attrs.add("internationalISDNNumber");
-        attrs.add("facsimileTelephoneNumber");
-        attrs.add("preferredDeliveryMethod");
-        attrs.add("street");
-        attrs.add("postOfficeBox");
-        attrs.add("postalCode");
-        attrs.add("postalAddress");
-        attrs.add("physicalDeliveryOfficeName");
-        attrs.add("st");
-        attrs.add("l");
-
-        builder.addObjectClass("2.5.6.10", Collections.singletonList("residentialPerson"),
-                EMPTY_STRING, false, Collections.singleton("person"), Collections.singleton("l"),
-                attrs, ObjectClassType.STRUCTURAL, RFC4519_ORIGIN, false);
-
-        builder.addObjectClass("1.3.6.1.1.3.1", Collections.singletonList("uidObject"),
-                EMPTY_STRING, false, Collections.singleton(TOP_OBJECTCLASS_NAME), Collections
-                        .singleton("uid"), attrs, ObjectClassType.AUXILIARY, RFC4519_ORIGIN, false);
+        builder.buildObjectClass("1.3.6.1.1.3.1")
+                .names("uidObject")
+                .type(AUXILIARY)
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("uid")
+                .optionalAttributes("businessCategory", "x121Address", "registeredAddress", "destinationIndicator",
+                        "preferredDeliveryMethod", "telexNumber", "teletexTerminalIdentifier", "telephoneNumber",
+                        "internationalISDNNumber", "facsimileTelephoneNumber", "preferredDeliveryMethod", "street",
+                        "postOfficeBox", "postalCode", "postalAddress", "physicalDeliveryOfficeName", "st", "l")
+                .extraProperties(RFC4519_ORIGIN)
+                .addToSchema();
     }
 
     private static void addRFC4523(final SchemaBuilder builder) {
@@ -811,53 +719,79 @@
                 null, null, SYNTAX_CERTLIST_OID, false, false, false,
                 AttributeUsage.USER_APPLICATIONS, RFC4523_ORIGIN, false);
 
-        builder.addObjectClass("2.5.6.21", Collections.singletonList("pkiUser"),
-                "X.509 PKI User", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
-                Collections.singleton("userCertificate"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.21")
+                .names("pkiUser")
+                .type(AUXILIARY)
+                .description("X.509 PKI User")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .optionalAttributes("userCertificate")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        Set<String> attrs = new HashSet<String>();
-        attrs.add("cACertificate");
-        attrs.add("certificateRevocationList");
-        attrs.add("authorityRevocationList");
-        attrs.add("crossCertificatePair");
+        builder.buildObjectClass("2.5.6.22")
+                .names("pkiCA")
+                .type(AUXILIARY)
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .description("X.509 PKI Certificate Authority")
+                .optionalAttributes("cACertificate", "certificateRevocationList",
+                    "authorityRevocationList", "crossCertificatePair")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.22", Collections.singletonList("pkiCA"),
-                "X.509 PKI Certificate Authority", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
-                EMPTY_STRING_SET, attrs, ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.19")
+                .names("cRLDistributionPoint")
+                .description("X.509 CRL distribution point")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("cn")
+                .optionalAttributes("certificateRevocationList", "authorityRevocationList", "deltaRevocationList")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("certificateRevocationList");
-        attrs.add("authorityRevocationList");
-        attrs.add("deltaRevocationList");
+        builder.buildObjectClass("2.5.6.23")
+                .names("deltaCRL")
+                .type(AUXILIARY)
+                .description("X.509 delta CRL")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .optionalAttributes("deltaRevocationList")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.19", Collections.singletonList("cRLDistributionPoint"),
-                "X.509 CRL distribution point", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
-                Collections.singleton("cn"), attrs, ObjectClassType.STRUCTURAL, RFC4523_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.15")
+                .names("strongAuthenticationUser")
+                .type(AUXILIARY)
+                .description("X.521 strong authentication user")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("userCertificate")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.23", Collections.singletonList("deltaCRL"),
-                "X.509 delta CRL", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
-                Collections.singleton("deltaRevocationList"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
-        builder.addObjectClass("2.5.6.15", Collections.singletonList("strongAuthenticationUser"),
-                "X.521 strong authentication user", false, Collections.singleton(TOP_OBJECTCLASS_NAME),
-                Collections.singleton("userCertificate"), EMPTY_STRING_SET, ObjectClassType.AUXILIARY,
-                RFC4523_ORIGIN, false);
-        builder.addObjectClass("2.5.6.18", Collections.singletonList("userSecurityInformation"),
-                "X.521 user security information", false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
-                Collections.singleton("supportedAlgorithms"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.18")
+                .names("userSecurityInformation")
+                .type(AUXILIARY)
+                .description("X.521 user security information")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .optionalAttributes("supportedAlgorithms")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        attrs = new HashSet<String>();
-        attrs.add("authorityRevocationList");
-        attrs.add("certificateRevocationList");
-        attrs.add("cACertificate");
+        builder.buildObjectClass("2.5.6.16")
+                .names("certificationAuthority")
+                .type(AUXILIARY)
+                .description("X.509 certificate authority")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("authorityRevocationList", "certificateRevocationList", "cACertificate")
+                .optionalAttributes("crossCertificatePair")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.16", Collections.singletonList("certificationAuthority"),
-                "X.509 certificate authority", false, Collections.singleton(TOP_OBJECTCLASS_NAME), attrs,
-                Collections.singleton("crossCertificatePair"), ObjectClassType.AUXILIARY, RFC4523_ORIGIN, false);
-
-        builder.addObjectClass("2.5.6.16.2", Collections.singletonList("certificationAuthority-V2"),
-                "X.509 certificate authority, version 2", false, Collections.singleton("certificationAuthority"),
-                EMPTY_STRING_SET, Collections.singleton("deltaRevocationList"), ObjectClassType.AUXILIARY,
-                RFC4523_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.16.2")
+                .names("certificationAuthority-V2")
+                .type(AUXILIARY)
+                .description("X.509 certificate authority, version 2")
+                .superiorObjectClasses("certificationAuthority")
+                .optionalAttributes("deltaRevocationList")
+                .extraProperties(RFC4523_ORIGIN)
+                .addToSchema();
     }
 
     private static void addRFC4530(final SchemaBuilder builder) {
@@ -1267,32 +1201,36 @@
     }
 
     private static void defaultObjectClasses(final SchemaBuilder builder) {
-        builder.addObjectClass(TOP_OBJECTCLASS_OID,
-                Collections.singletonList(TOP_OBJECTCLASS_NAME), TOP_OBJECTCLASS_DESCRIPTION,
-                false, EMPTY_STRING_SET, Collections.singleton("objectClass"), EMPTY_STRING_SET,
-                ObjectClassType.ABSTRACT, RFC4512_ORIGIN, false);
+        builder.buildObjectClass(TOP_OBJECTCLASS_OID)
+                .names(TOP_OBJECTCLASS_NAME)
+                .type(ABSTRACT)
+                .description(TOP_OBJECTCLASS_DESCRIPTION)
+                .requiredAttributes("objectClass")
+                .extraProperties(RFC4512_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass("2.5.6.1", Collections.singletonList("alias"), EMPTY_STRING, false,
-                Collections.singleton("top"), Collections.singleton("aliasedObjectName"),
-                EMPTY_STRING_SET, ObjectClassType.STRUCTURAL, RFC4512_ORIGIN, false);
+        builder.buildObjectClass("2.5.6.1")
+                .names("alias")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("aliasedObjectName")
+                .extraProperties(RFC4512_ORIGIN)
+                .addToSchema();
 
-        builder.addObjectClass(EXTENSIBLE_OBJECT_OBJECTCLASS_OID, Collections
-                .singletonList(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME), EMPTY_STRING, false,
-                Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET, EMPTY_STRING_SET,
-                ObjectClassType.AUXILIARY, RFC4512_ORIGIN, false);
+        builder.buildObjectClass(EXTENSIBLE_OBJECT_OBJECTCLASS_OID)
+                .names(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME)
+                .type(AUXILIARY)
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .extraProperties(RFC4512_ORIGIN)
+                .addToSchema();
 
-        final Set<String> subschemaAttrs = new HashSet<String>();
-        subschemaAttrs.add("dITStructureRules");
-        subschemaAttrs.add("nameForms");
-        subschemaAttrs.add("ditContentRules");
-        subschemaAttrs.add("objectClasses");
-        subschemaAttrs.add("attributeTypes");
-        subschemaAttrs.add("matchingRules");
-        subschemaAttrs.add("matchingRuleUse");
-
-        builder.addObjectClass("2.5.20.1", Collections.singletonList("subschema"), EMPTY_STRING,
-                false, Collections.singleton(TOP_OBJECTCLASS_NAME), EMPTY_STRING_SET,
-                subschemaAttrs, ObjectClassType.AUXILIARY, RFC4512_ORIGIN, false);
+        builder.buildObjectClass("2.5.20.1")
+                .names("subschema")
+                .type(AUXILIARY)
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .optionalAttributes("dITStructureRules", "nameForms", "ditContentRules", "objectClasses",
+                    "attributeTypes", "matchingRules", "matchingRuleUse")
+                .extraProperties(RFC4512_ORIGIN)
+                .addToSchema();
     }
 
     private static void defaultSyntaxes(final SchemaBuilder builder) {
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
index cd48852..82563d3 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/ObjectClass.java
@@ -22,24 +22,31 @@
  *
  *
  *      Copyright 2009 Sun Microsystems, Inc.
+ *      Portions copyright 2015 ForgeRock AS.
  */
 
 package org.forgerock.opendj.ldap.schema;
 
-import static com.forgerock.opendj.ldap.CoreMessages.*;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_NAME;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
-import static org.forgerock.opendj.ldap.schema.SchemaConstants.TOP_OBJECTCLASS_NAME;
-
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 import org.forgerock.i18n.LocalizableMessage;
-import org.forgerock.util.Reject;
+
+import static java.util.Arrays.*;
+import static java.util.Collections.*;
+
+import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+import static org.forgerock.opendj.ldap.schema.SchemaUtils.*;
+
+import static com.forgerock.opendj.ldap.CoreMessages.*;
 
 /**
  * This class defines a data structure for storing and interacting with an
@@ -51,6 +58,308 @@
  * accessed via their getters or via the {@link #toString()} methods.
  */
 public final class ObjectClass extends SchemaElement {
+
+    /** A fluent API for incrementally constructing object classes. */
+    public static final class Builder extends SchemaElementBuilder<Builder> {
+        private boolean isObsolete;
+        private final List<String> names = new LinkedList<String>();
+        private String oid;
+        private final Set<String> optionalAttributes = new LinkedHashSet<String>();
+        private final Set<String> requiredAttributes = new LinkedHashSet<String>();
+        private final Set<String> superiorClasses = new LinkedHashSet<String>();
+        private ObjectClassType type = null;
+
+        Builder(final ObjectClass oc, final SchemaBuilder builder) {
+            super(builder, oc);
+            this.oid = oc.oid;
+            this.names.addAll(oc.names);
+            this.isObsolete = oc.isObsolete;
+            this.type = oc.objectClassType;
+            this.superiorClasses.addAll(oc.superiorClassOIDs);
+            this.requiredAttributes.addAll(oc.requiredAttributeOIDs);
+            this.optionalAttributes.addAll(optionalAttributes);
+        }
+
+        Builder(final String oid, final SchemaBuilder builder) {
+            super(builder);
+            oid(oid);
+        }
+
+        /**
+         * Adds this object class to the schema overwriting any existing object class
+         * with the same numeric OID.
+         *
+         * @return The parent schema builder.
+         */
+        public SchemaBuilder addToSchemaOverwrite() {
+            return getSchemaBuilder().addObjectClass(new ObjectClass(this), true);
+        }
+
+        /**
+         * Adds this object class to the schema, throwing an
+         * {@code  ConflictingSchemaElementException} if there is an existing
+         * object class with the same numeric OID.
+         *
+         * @return The parent schema builder.
+         * @throws ConflictingSchemaElementException
+         *             If there is an existing object class with the same numeric
+         *             OID.
+         */
+        public SchemaBuilder addToSchema() {
+            return getSchemaBuilder().addObjectClass(new ObjectClass(this), false);
+        }
+
+        @Override
+        public Builder description(final String description) {
+            return description0(description);
+        }
+
+        @Override
+        public Builder extraProperties(final Map<String, List<String>> extraProperties) {
+            return extraProperties0(extraProperties);
+        }
+
+        @Override
+        public Builder extraProperties(final String extensionName, final String... extensionValues) {
+            return extraProperties0(extensionName, extensionValues);
+        }
+
+        @Override
+        Builder getThis() {
+            return this;
+        }
+
+        /**
+         * Adds the provided user friendly names.
+         *
+         * @param names
+         *            The user friendly names.
+         * @return This builder.
+         */
+        public Builder names(final Collection<String> names) {
+            this.names.addAll(names);
+            return this;
+        }
+
+        /**
+         * Adds the provided user friendly names.
+         *
+         * @param names
+         *            The user friendly names.
+         * @return This builder.
+         */
+        public Builder names(final String... names) {
+            return names(asList(names));
+        }
+
+        /**
+         * Specifies whether this schema element is obsolete.
+         *
+         * @param isObsolete
+         *            {@code true} if this schema element is obsolete
+         *            (default is {@code false}).
+         * @return This builder.
+         */
+        public Builder obsolete(final boolean isObsolete) {
+            this.isObsolete = isObsolete;
+            return this;
+        }
+
+        /**
+         * Sets the numeric OID which uniquely identifies this object class.
+         *
+         * @param oid
+         *            The numeric OID.
+         * @return This builder.
+         */
+        public Builder oid(final String oid) {
+            this.oid = oid;
+            return this;
+        }
+
+        /**
+         * Adds the provided optional attributes.
+         *
+         * @param attributeNamesOrOIDs
+         *      The list of optional attribute names or OIDs.
+         * @return This builder.
+         */
+        public Builder optionalAttributes(final Collection<String> attributeNamesOrOIDs) {
+            this.optionalAttributes.addAll(attributeNamesOrOIDs);
+            return this;
+        }
+
+        /**
+         * Adds the provided optional attributes.
+         *
+         * @param attributeNamesOrOIDs
+         *      The list of optional attribute names or OIDs.
+         * @return This builder.
+         */
+        public Builder optionalAttributes(final String... attributeNamesOrOIDs) {
+            this.optionalAttributes.addAll(asList(attributeNamesOrOIDs));
+            return this;
+        }
+
+        @Override
+        public Builder removeAllExtraProperties() {
+            return removeAllExtraProperties0();
+        }
+
+        @Override
+        public Builder removeExtraProperty(final String extensionName, final String... extensionValues) {
+            return removeExtraProperty0(extensionName, extensionValues);
+        }
+
+        /**
+         * Removes all user defined names.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllNames() {
+            this.names.clear();
+            return this;
+        }
+
+        /**
+         * Removes all optional attributes.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllOptionalAttributes() {
+            this.optionalAttributes.clear();
+            return this;
+        }
+
+        /**
+         * Removes all required attributes.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllRequiredAttributes() {
+            this.requiredAttributes.clear();
+            return this;
+        }
+
+        /**
+         * Removes all superior object class.
+         *
+         * @return This builder.
+         */
+        public Builder removeAllSuperiorObjectClass() {
+            this.superiorClasses.clear();
+            return this;
+        }
+
+        /**
+         * 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;
+        }
+
+        /**
+         * Removes the provided optional attribute.
+         *
+         * @param attributeNameOrOID
+         *            The optional attribute name or OID to be removed.
+         * @return This builder.
+         */
+        public Builder removeOptionalAttribute(String attributeNameOrOID) {
+            this.optionalAttributes.remove(attributeNameOrOID);
+            return this;
+        }
+
+        /**
+         * Removes the provided required attribute.
+         *
+         * @param attributeNameOrOID
+         *            The provided required attribute name or OID to be removed.
+         * @return This builder.
+         */
+        public Builder removeRequiredAttribute(String attributeNameOrOID) {
+            this.optionalAttributes.remove(attributeNameOrOID);
+            return this;
+        }
+
+        /**
+         * Removes the provided superior object class.
+         *
+         * @param objectClassNameOrOID
+         *            The superior object class name or OID to be removed.
+         * @return This builder.
+         */
+        public Builder removeSuperiorObjectClass(String objectClassNameOrOID) {
+            this.superiorClasses.remove(objectClassNameOrOID);
+            return this;
+        }
+
+        /**
+         * Adds the provided required attributes.
+         *
+         * @param attributeNamesOrOIDs
+         *      The list of required attribute names or OIDs.
+         * @return This builder.
+         */
+        public Builder requiredAttributes(final Collection<String> attributeNamesOrOIDs) {
+            this.requiredAttributes.addAll(attributeNamesOrOIDs);
+            return this;
+        }
+
+        /**
+         * Adds the provided required attributes.
+         *
+         * @param attributeNamesOrOIDs
+         *      The list of required attribute names or OIDs.
+         * @return This builder.
+         */
+        public Builder requiredAttributes(final String... attributeNamesOrOIDs) {
+            this.requiredAttributes.addAll(asList(attributeNamesOrOIDs));
+            return this;
+        }
+
+        /**
+         * Adds the provided superior object classes.
+         *
+         * @param objectClassNamesOrOIDs
+         *      The list of superior object classes names or OIDs.
+         * @return This builder.
+         */
+        public Builder superiorObjectClasses(final Collection<String> objectClassNamesOrOIDs) {
+            this.superiorClasses.addAll(objectClassNamesOrOIDs);
+            return this;
+        }
+
+        /**
+         * Adds the provided superior object classes.
+         *
+         * @param objectClassNamesOrOIDs
+         *      The list of superior object classes names or OIDs.
+         * @return This builder.
+         */
+        public Builder superiorObjectClasses(final String... objectClassNamesOrOIDs) {
+            this.superiorClasses.addAll(asList(objectClassNamesOrOIDs));
+            return this;
+        }
+
+        /**
+         * Sets the type of this object class.
+         *
+         * @param type
+         *      The object class type.
+         * @return This builder.
+         */
+        public Builder type(final ObjectClassType type) {
+            this.type = type;
+            return this;
+        }
+    }
+
     /** The OID that may be used to reference this definition. */
     private final String oid;
 
@@ -72,11 +381,11 @@
     /** The set of optional attribute types for this objectclass. */
     private final Set<String> optionalAttributeOIDs;
 
-    private Set<ObjectClass> superiorClasses = Collections.emptySet();
-    private Set<AttributeType> declaredRequiredAttributes = Collections.emptySet();
-    private Set<AttributeType> requiredAttributes = Collections.emptySet();
-    private Set<AttributeType> declaredOptionalAttributes = Collections.emptySet();
-    private Set<AttributeType> optionalAttributes = Collections.emptySet();
+    private Set<ObjectClass> superiorClasses = emptySet();
+    private Set<AttributeType> declaredRequiredAttributes = emptySet();
+    private Set<AttributeType> requiredAttributes = emptySet();
+    private Set<AttributeType> declaredOptionalAttributes = emptySet();
+    private Set<AttributeType> optionalAttributes = emptySet();
 
     /** Indicates whether or not validation has been performed. */
     private boolean needsValidating = true;
@@ -84,23 +393,6 @@
     /** The indicates whether or not validation failed. */
     private boolean isValid;
 
-    ObjectClass(final String oid, final List<String> names, final String description,
-            final boolean obsolete, final Set<String> superiorClassOIDs,
-            final Set<String> requiredAttributeOIDs, final Set<String> optionalAttributeOIDs,
-            final ObjectClassType objectClassType, final Map<String, List<String>> extraProperties,
-            final String definition) {
-        super(description, extraProperties, definition);
-
-        Reject.ifNull(oid, names, superiorClassOIDs, requiredAttributeOIDs, optionalAttributeOIDs, objectClassType);
-        this.oid = oid;
-        this.names = names;
-        this.isObsolete = obsolete;
-        this.superiorClassOIDs = superiorClassOIDs;
-        this.objectClassType = objectClassType;
-        this.requiredAttributeOIDs = requiredAttributeOIDs;
-        this.optionalAttributeOIDs = optionalAttributeOIDs;
-    }
-
     /**
      * Construct a extensibleObject object class where the set of allowed
      * attribute types of this object class is implicitly the set of all
@@ -111,15 +403,31 @@
      * @param extraProperties
      *            The map of "extra" properties for this schema definition
      */
-    ObjectClass(final String description, final Map<String, List<String>> extraProperties) {
-        super(description, extraProperties, null);
-        this.oid = EXTENSIBLE_OBJECT_OBJECTCLASS_OID;
-        this.names = Collections.singletonList(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME);
-        this.isObsolete = false;
-        this.superiorClassOIDs = Collections.singleton(TOP_OBJECTCLASS_NAME);
-        this.objectClassType = ObjectClassType.AUXILIARY;
-        this.requiredAttributeOIDs = Collections.emptySet();
-        this.optionalAttributeOIDs = Collections.emptySet();
+    static ObjectClass newExtensibleObjectObjectClass(final String description,
+        final Map<String, List<String>> extraProperties, final SchemaBuilder builder) {
+        return new ObjectClass(new Builder(EXTENSIBLE_OBJECT_OBJECTCLASS_OID, builder)
+               .description(description)
+               .extraProperties(extraProperties)
+               .names(EXTENSIBLE_OBJECT_OBJECTCLASS_NAME)
+               .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+               .type(AUXILIARY));
+    }
+
+
+    private ObjectClass(final Builder builder) {
+        super(builder);
+
+        if (builder.oid == null || builder.oid.isEmpty()) {
+            throw new IllegalArgumentException("An OID must be specified.");
+        }
+
+        this.oid = builder.oid;
+        this.names = unmodifiableCopyOfList(builder.names);
+        this.isObsolete = builder.isObsolete;
+        this.superiorClassOIDs = unmodifiableCopyOfSet(builder.superiorClasses);
+        this.objectClassType = builder.type;
+        this.requiredAttributeOIDs = unmodifiableCopyOfSet(builder.requiredAttributes);
+        this.optionalAttributeOIDs = unmodifiableCopyOfSet(builder.optionalAttributes);
     }
 
     /**
@@ -198,8 +506,7 @@
      * @return The objectclass type for this objectclass.
      */
     public ObjectClassType getObjectClassType() {
-
-        return objectClassType;
+        return objectClassType != null ? objectClassType : STRUCTURAL;
     }
 
     /**
@@ -208,7 +515,6 @@
      * @return The OID for this schema definition.
      */
     public String getOID() {
-
         return oid;
     }
 
@@ -356,12 +662,6 @@
         return isRequired(attributeType) || isOptional(attributeType);
     }
 
-    ObjectClass duplicate() {
-        return new ObjectClass(oid, names, getDescription(), isObsolete, superiorClassOIDs,
-                requiredAttributeOIDs, optionalAttributeOIDs, objectClassType,
-                getExtraProperties(), toString());
-    }
-
     @Override
     void toStringContent(final StringBuilder buffer) {
         buffer.append(oid);
@@ -473,7 +773,7 @@
 
         // Init a flag to check to inheritance from top (only needed for
         // structural object classes) per RFC 4512
-        boolean derivesTop = objectClassType != ObjectClassType.STRUCTURAL;
+        boolean derivesTop = getObjectClassType() != ObjectClassType.STRUCTURAL;
 
         if (!superiorClassOIDs.isEmpty()) {
             superiorClasses = new HashSet<ObjectClass>(superiorClassOIDs.size());
@@ -491,14 +791,15 @@
 
                 // Make sure that the inheritance configuration is acceptable.
                 final ObjectClassType superiorType = superiorClass.getObjectClassType();
-                switch (objectClassType) {
+                final ObjectClassType type = getObjectClassType();
+                switch (type) {
                 case ABSTRACT:
                     // Abstract classes may only inherit from other abstract
                     // classes.
                     if (superiorType != ObjectClassType.ABSTRACT) {
                         final LocalizableMessage message =
                                 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
-                                        getNameOrOID(), objectClassType.toString(), superiorType
+                                        getNameOrOID(), type.toString(), superiorType
                                                 .toString(), superiorClass.getNameOrOID());
                         failValidation(invalidSchemaElements, warnings, message);
                         return false;
@@ -513,7 +814,7 @@
                             && superiorType != ObjectClassType.AUXILIARY) {
                         final LocalizableMessage message =
                                 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
-                                        getNameOrOID(), objectClassType.toString(), superiorType
+                                        getNameOrOID(), type.toString(), superiorType
                                                 .toString(), superiorClass.getNameOrOID());
                         failValidation(invalidSchemaElements, warnings, message);
                         return false;
@@ -527,7 +828,7 @@
                             && superiorType != ObjectClassType.STRUCTURAL) {
                         final LocalizableMessage message =
                                 WARN_ATTR_SYNTAX_OBJECTCLASS_INVALID_SUPERIOR_TYPE1.get(
-                                        getNameOrOID(), objectClassType.toString(), superiorType
+                                        getNameOrOID(), type.toString(), superiorType
                                                 .toString(), superiorClass.getNameOrOID());
                         failValidation(invalidSchemaElements, warnings, message);
                         return false;
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
index 14a431f..d9653ca 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/SchemaBuilder.java
@@ -22,7 +22,7 @@
  *
  *
  *      Copyright 2009-2010 Sun Microsystems, Inc.
- *      Portions Copyright 2011-2014 ForgeRock AS
+ *      Portions Copyright 2011-2015 ForgeRock AS
  *      Portions Copyright 2014 Manuel Gaupp
  */
 package org.forgerock.opendj.ldap.schema;
@@ -66,11 +66,16 @@
 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.*;
 
@@ -1143,7 +1148,7 @@
      *
      * @param oid
      *            The OID of the matching rule definition.
-     * @return A builder to continue building the NameForm.
+     * @return A builder to continue building the MatchingRule.
      */
     public MatchingRule.Builder buildMatchingRule(final String oid) {
         lazyInitBuilder();
@@ -1394,6 +1399,39 @@
     }
 
     /**
+     * Returns an object class builder whose fields are initialized to the
+     * values of the provided object class. This method should be used when
+     * duplicating object classes from external schemas or when modifying
+     * existing object classes.
+     *
+     * @param objectClass
+     *            The object class source.
+     * @return A builder to continue building the ObjectClass.
+     */
+    public ObjectClass.Builder buildObjectClass(final ObjectClass objectClass) {
+        lazyInitBuilder();
+        return new ObjectClass.Builder(objectClass, this);
+    }
+
+    /**
+     * Returns a builder which can be used for incrementally constructing a new
+     * object class before adding it to the schema. Example usage:
+     *
+     * <pre>
+     * SchemaBuilder builder = ...;
+     * builder.buildObjectClass("objectclass-oid").name("object class name").addToSchema();
+     * </pre>
+     *
+     * @param oid
+     *            The OID of the object class definition.
+     * @return A builder to continue building the ObjectClass.
+     */
+    public ObjectClass.Builder buildObjectClass(final String oid) {
+        lazyInitBuilder();
+        return new ObjectClass.Builder(oid, this);
+    }
+
+    /**
      * Duplicates the syntax.
      *
      * @param syntax
@@ -1470,17 +1508,11 @@
             // parenthesis.
             reader.skipWhitespaces();
 
-            // The next set of characters must be the OID.
+            // The next set of characters is the OID.
             final String oid = readOID(reader, allowsMalformedNamesAndOptions());
-
-            List<String> names = Collections.emptyList();
-            String description = "".intern();
-            boolean isObsolete = false;
-            Set<String> superiorClasses = Collections.emptySet();
-            Set<String> requiredAttributes = Collections.emptySet();
-            Set<String> optionalAttributes = Collections.emptySet();
-            ObjectClassType objectClassType = ObjectClassType.STRUCTURAL;
-            Map<String, List<String>> extraProperties = Collections.emptyMap();
+            Set<String> superiorClasses = emptySet();
+            ObjectClassType ocType = null;
+            ObjectClass.Builder ocBuilder = new ObjectClass.Builder(oid, this).definition(definition);
 
             // At this point, we should have a pretty specific syntax that
             // describes what may come next, but some of the components are
@@ -1497,47 +1529,39 @@
                     // No more tokens.
                     break;
                 } else if ("name".equalsIgnoreCase(tokenName)) {
-                    names = readNameDescriptors(reader, allowsMalformedNamesAndOptions());
+                    ocBuilder.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);
+                    ocBuilder.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.
+                    ocBuilder.obsolete(true);
                 } else if ("sup".equalsIgnoreCase(tokenName)) {
                     superiorClasses = readOIDs(reader, allowsMalformedNamesAndOptions());
                 } else if ("abstract".equalsIgnoreCase(tokenName)) {
                     // This indicates that entries must not include this
                     // objectclass unless they also include a non-abstract
-                    // objectclass that inherits from this class. We do not need
-                    // any more parsing for this token.
-                    objectClassType = ObjectClassType.ABSTRACT;
+                    // objectclass that inherits from this class.
+                    ocType = ABSTRACT;
                 } else if ("structural".equalsIgnoreCase(tokenName)) {
-                    // This indicates that this is a structural objectclass. We
-                    // do not need any more parsing for this token.
-                    objectClassType = ObjectClassType.STRUCTURAL;
+                    ocType = STRUCTURAL;
                 } else if ("auxiliary".equalsIgnoreCase(tokenName)) {
-                    // This indicates that this is an auxiliary objectclass. We
-                    // do not need any more parsing for this token.
-                    objectClassType = ObjectClassType.AUXILIARY;
+                    ocType = AUXILIARY;
                 } else if ("must".equalsIgnoreCase(tokenName)) {
-                    requiredAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
+                    ocBuilder.requiredAttributes(readOIDs(reader, allowsMalformedNamesAndOptions()));
                 } else if ("may".equalsIgnoreCase(tokenName)) {
-                    optionalAttributes = readOIDs(reader, allowsMalformedNamesAndOptions());
+                    ocBuilder.optionalAttributes(readOIDs(reader, allowsMalformedNamesAndOptions()));
                 } else if (tokenName.matches("^X-[A-Za-z_-]+$")) {
                     // This must be a non-standard property and it must be
                     // followed by either a single definition in single quotes
                     // or an open parenthesis followed by one or more values in
                     // single quotes separated by spaces followed by a close
                     // parenthesis.
-                    if (extraProperties.isEmpty()) {
-                        extraProperties = new HashMap<String, List<String>>();
-                    }
-                    extraProperties.put(tokenName, readExtensions(reader));
+                    final List<String> extensions = readExtensions(reader);
+                    ocBuilder.extraProperties(tokenName, extensions.toArray(new String[extensions.size()]));
                 } else {
                     throw new LocalizedIllegalArgumentException(
                         ERR_ATTR_SYNTAX_OBJECTCLASS_ILLEGAL_TOKEN1.get(definition, tokenName));
@@ -1545,81 +1569,21 @@
             }
 
             if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
-                addObjectClass(new ObjectClass(description, extraProperties), overwrite);
+                addObjectClass(newExtensibleObjectObjectClass(
+                    ocBuilder.getDescription(), ocBuilder.getExtraProperties(), this), overwrite);
+                return this;
             } else {
-                if (objectClassType == ObjectClassType.STRUCTURAL && superiorClasses.isEmpty()) {
-                    superiorClasses = Collections.singleton(TOP_OBJECTCLASS_NAME);
+                if (ocType == STRUCTURAL && superiorClasses.isEmpty()) {
+                    superiorClasses = singleton(TOP_OBJECTCLASS_NAME);
                 }
-
-                if (!extraProperties.isEmpty()) {
-                    extraProperties = Collections.unmodifiableMap(extraProperties);
-                }
-
-                addObjectClass(new ObjectClass(oid, names, description, isObsolete,
-                        superiorClasses, requiredAttributes, optionalAttributes, objectClassType,
-                        extraProperties, definition), overwrite);
+                ocBuilder.superiorObjectClasses(superiorClasses)
+                         .type(ocType);
+                return overwrite ? ocBuilder.addToSchemaOverwrite() : ocBuilder.addToSchema();
             }
         } catch (final DecodeException e) {
-            final LocalizableMessage msg =
-                    ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, e.getMessageObject());
-            throw new LocalizedIllegalArgumentException(msg, e.getCause());
+            throw new LocalizedIllegalArgumentException(
+                ERR_ATTR_SYNTAX_OBJECTCLASS_INVALID1.get(definition, e.getMessageObject()), e.getCause());
         }
-        return this;
-    }
-
-    /**
-     * Adds the provided object class definition to this schema builder.
-     *
-     * @param oid
-     *            The OID of the object class definition.
-     * @param names
-     *            The user-friendly names of the object class definition.
-     * @param description
-     *            The description of the object class definition.
-     * @param obsolete
-     *            {@code true} if the object class definition is obsolete,
-     *            otherwise {@code false}.
-     * @param superiorClassOIDs
-     *            A list of direct superclasses of the object class.
-     * @param requiredAttributeOIDs
-     *            A list of attribute types that entries must contain.
-     * @param optionalAttributeOIDs
-     *            A list of attribute types that entries may contain.
-     * @param objectClassType
-     *            The type of the object class.
-     * @param extraProperties
-     *            A map containing additional properties associated with the
-     *            object class definition.
-     * @param overwrite
-     *            {@code true} if any existing object class 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 addObjectClass(final String oid, final List<String> names,
-            final String description, final boolean obsolete, Set<String> superiorClassOIDs,
-            final Set<String> requiredAttributeOIDs, final Set<String> optionalAttributeOIDs,
-            final ObjectClassType objectClassType, final Map<String, List<String>> extraProperties,
-            final boolean overwrite) {
-        lazyInitBuilder();
-
-        if (EXTENSIBLE_OBJECT_OBJECTCLASS_OID.equals(oid)) {
-            addObjectClass(new ObjectClass(description,
-                    unmodifiableCopyOfExtraProperties(extraProperties)), overwrite);
-        } else {
-            if (objectClassType == ObjectClassType.STRUCTURAL && superiorClassOIDs.isEmpty()) {
-                superiorClassOIDs = Collections.singleton(TOP_OBJECTCLASS_NAME);
-            }
-
-            addObjectClass(new ObjectClass(oid, unmodifiableCopyOfList(names), description,
-                    obsolete, unmodifiableCopyOfSet(superiorClassOIDs),
-                    unmodifiableCopyOfSet(requiredAttributeOIDs),
-                    unmodifiableCopyOfSet(optionalAttributeOIDs), objectClassType,
-                    unmodifiableCopyOfExtraProperties(extraProperties), null), overwrite);
-        }
-        return this;
     }
 
     /**
@@ -2535,7 +2499,7 @@
         return this;
     }
 
-    private void addObjectClass(final ObjectClass oc, final boolean overwrite) {
+    SchemaBuilder addObjectClass(final ObjectClass oc, final boolean overwrite) {
         ObjectClass conflictingOC;
         if (numericOID2ObjectClasses.containsKey(oc.getOID())) {
             conflictingOC = numericOID2ObjectClasses.get(oc.getOID());
@@ -2562,6 +2526,8 @@
                 classes.add(oc);
             }
         }
+
+        return this;
     }
 
     private void addSchema0(final Schema schema, final boolean overwrite) {
@@ -2594,7 +2560,7 @@
         }
 
         for (final ObjectClass objectClass : schema.getObjectClasses()) {
-            addObjectClass(objectClass.duplicate(), overwrite);
+            addObjectClass(objectClass, overwrite);
         }
 
         for (final NameForm nameForm : schema.getNameForms()) {
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java
new file mode 100644
index 0000000..37ad0a0
--- /dev/null
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/ObjectClassBuilderTestCase.java
@@ -0,0 +1,208 @@
+/*
+ * 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 java.util.List;
+import java.util.Set;
+
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+import static java.util.Collections.*;
+
+import static org.fest.assertions.Assertions.*;
+import static org.fest.assertions.MapAssert.*;
+import static org.forgerock.opendj.ldap.schema.ObjectClassType.*;
+import static org.forgerock.opendj.ldap.schema.Schema.*;
+import static org.forgerock.opendj.ldap.schema.SchemaConstants.*;
+
+public class ObjectClassBuilderTestCase extends AbstractSchemaTestCase {
+
+    @DataProvider
+    Object[][] validObjectClasses() {
+        // OID, obsolete, names, optional attributes, required attributes,
+        // superior object classes, type, description,
+        // extra property name, extra property values, overwrite
+        return new Object[][] {
+            // Basic object class
+            { "1.2.3.4", false, singletonList("MyObjectClass"), emptySet(), singleton("cn"),
+                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "MyObjectClass description.", "New extra property",
+                "New extra value", false },
+            // Allowed overrides existing core schema object class groupOfNames
+            { "2.5.6.9", false, singletonList("groupOfFirstNames"), emptySet(), singleton("cn"),
+                singleton(TOP_OBJECTCLASS_NAME), AUXILIARY, "MyObjectClass description.", "New extra property",
+                "New extra value", true },
+            // No name provided, should be validated
+            { "1.2.3.4", false, emptyList(), singleton("name"), singleton("cn"),
+                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "MyObjectClass description.", "New extra property",
+                "New extra value", false },
+            // Empty description, should be validated
+            { "1.2.3.4", false, emptyList(), singleton("name"), singleton("cn"),
+                singleton(TOP_OBJECTCLASS_NAME), STRUCTURAL, "", "New extra property",
+                "New extra value", false },
+        };
+    }
+
+    @Test(dataProvider = "validObjectClasses")
+    public void testValidOCBuilder(final String oid, final boolean isObsolete, final List<String> names,
+            final Set<String> optionalAttributeOIDs, final Set<String> requiredAttributesOIDs,
+            final Set<String> superiorClassOIDs, final ObjectClassType type, final String description,
+            final String extraPropertyName, final String extraPropertyValue, final boolean overwrite)
+            throws Exception {
+        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
+            .buildObjectClass(oid)
+            .description(description)
+            .obsolete(isObsolete)
+            .names(names)
+            .superiorObjectClasses(superiorClassOIDs)
+            .requiredAttributes(requiredAttributesOIDs)
+            .optionalAttributes(optionalAttributeOIDs)
+            .type(type)
+            .extraProperties(extraPropertyName, extraPropertyValue);
+
+        final Schema schema = overwrite ? ocBuilder.addToSchemaOverwrite().toSchema()
+                                        : ocBuilder.addToSchema().toSchema();
+
+        assertThat(schema.getWarnings()).isEmpty();
+        final ObjectClass oc = schema.getObjectClass(oid);
+        assertThat(oc).isNotNull();
+        assertThat(oc.getOID()).isEqualTo(oid);
+        assertThat(oc.getDescription()).isEqualTo(description);
+        assertThat(oc.isObsolete()).isEqualTo(isObsolete);
+        assertThat(oc.getNames()).containsOnly(names.toArray());
+        assertSchemaElementsContainsAll(oc.getSuperiorClasses(), superiorClassOIDs);
+        assertSchemaElementsContainsAll(oc.getRequiredAttributes(), requiredAttributesOIDs);
+        assertSchemaElementsContainsAll(oc.getOptionalAttributes(), optionalAttributeOIDs);
+        assertThat(oc.getObjectClassType()).isEqualTo(type);
+        assertThat(oc.getExtraProperties()).includes(entry(extraPropertyName, singletonList(extraPropertyValue)));
+    }
+
+    @Test
+    public void testOCBuilderDefaultValues() throws Exception {
+        final SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
+        final ObjectClass.Builder ocBuilder = sb.buildObjectClass("1.1.1.42")
+                .description("Default object class")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .names("defaultObjectClass");
+        final Schema schema = ocBuilder.addToSchema().toSchema();
+        assertThat(schema.getWarnings()).isEmpty();
+
+        final ObjectClass oc = schema.getObjectClass("defaultObjectClass");
+        assertThat(oc).isNotNull();
+        assertThat(oc.getOID()).isEqualTo("1.1.1.42");
+        assertThat(oc.getDescription()).isEqualTo("Default object class");
+        assertThat(oc.isObsolete()).isFalse();
+        assertThat(oc.getNames()).containsOnly("defaultObjectClass");
+        assertSchemaElementsContainsAll(oc.getSuperiorClasses(), TOP_OBJECTCLASS_NAME);
+        final Set<AttributeType> topReqAttrs = schema.getObjectClass(TOP_OBJECTCLASS_NAME).getRequiredAttributes();
+        assertThat(oc.getRequiredAttributes()).containsOnly(topReqAttrs.toArray());
+        assertThat(oc.getOptionalAttributes()).isEmpty();
+        assertThat(oc.getObjectClassType()).isEqualTo(STRUCTURAL);
+        assertThat(oc.getExtraProperties()).isEmpty();
+    }
+
+    @Test
+    public void testOCBuilderCopyConstructor() throws Exception {
+        final SchemaBuilder sb = new SchemaBuilder(getCoreSchema());
+        final ObjectClass.Builder ocBuilder = sb.buildObjectClass("1.1.1.42")
+                .description("Object class to duplicate")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .names("ObjectClassToDuplicate")
+                .requiredAttributes("name");
+        final Schema schema = ocBuilder.addToSchema().toSchema();
+        assertThat(schema.getWarnings()).isEmpty();
+
+        sb.buildObjectClass(schema.getObjectClass("ObjectClassToDuplicate"))
+                .oid("1.1.1.43")
+                .names("Copy")
+                .obsolete(true)
+                .addToSchemaOverwrite();
+        final Schema schemaCopy = sb.toSchema();
+        assertThat(schemaCopy.getWarnings()).isEmpty();
+
+        final ObjectClass ocCopy = schemaCopy.getObjectClass("Copy");
+        assertThat(ocCopy).isNotNull();
+        assertThat(ocCopy.getOID()).isEqualTo("1.1.1.43");
+        assertThat(ocCopy.getDescription()).isEqualTo("Object class to duplicate");
+        assertThat(ocCopy.isObsolete()).isTrue();
+        assertThat(ocCopy.getNames()).containsOnly("ObjectClassToDuplicate", "Copy");
+        assertSchemaElementsContainsAll(ocCopy.getSuperiorClasses(), TOP_OBJECTCLASS_NAME);
+        assertSchemaElementsContainsAll(ocCopy.getRequiredAttributes(), "name");
+        assertThat(ocCopy.getOptionalAttributes()).isEmpty();
+        assertThat(ocCopy.getObjectClassType()).isEqualTo(STRUCTURAL);
+        assertThat(ocCopy.getExtraProperties()).isEmpty();
+    }
+
+    @Test(expectedExceptions = ConflictingSchemaElementException.class)
+    public void testOCBuilderDoesNotAllowOverwrite() throws Exception {
+        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
+            .buildObjectClass("2.5.6.9")
+            .description("MyObjectClass description")
+            .names("groupOfFirstNames")
+            .requiredAttributes("cn")
+            .type(AUXILIARY)
+            .extraProperties("New extra property", "New extra value");
+
+        ocBuilder.addToSchema().toSchema();
+    }
+
+    @Test(expectedExceptions = IllegalArgumentException.class)
+    public void testOCBuilderDoesNotAllowEmptyOID() throws Exception {
+        final ObjectClass.Builder ocBuilder = new SchemaBuilder(getCoreSchema())
+            .buildObjectClass("")
+            .description("MyObjectClass description")
+            .names("MyObjectClass")
+            .requiredAttributes("cn")
+            .extraProperties("New extra property", "New extra value");
+
+        ocBuilder.addToSchema().toSchema();
+    }
+
+    private void assertSchemaElementsContainsAll(final Set<? extends SchemaElement> elements,
+            final Set<String> namesOrOIDs) throws Exception {
+        assertSchemaElementsContainsAll(elements, namesOrOIDs.toArray(new String[namesOrOIDs.size()]));
+    }
+
+
+    private void assertSchemaElementsContainsAll(final Set<? extends SchemaElement> elements,
+            final String... namesOrOIDs) throws Exception {
+        for (final String nameOrOID : namesOrOIDs) {
+            assertThat(assertSchemaElementsContains(elements, nameOrOID)).isTrue();
+        }
+    }
+
+    private boolean assertSchemaElementsContains(final Set<? extends SchemaElement> elements, final String nameOrOID) {
+        for (final SchemaElement element : elements) {
+            final String oid = element instanceof AttributeType ? ((AttributeType) element).getNameOrOID()
+                                                            : ((ObjectClass) element).getNameOrOID();
+            if (oid.equals(nameOrOID)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
index 4982cdc..fe509c9 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/SchemaBuilderTestCase.java
@@ -21,13 +21,11 @@
  * CDDL HEADER END
  *
  *
- *      Portions Copyright 2012-2014 ForgeRock AS.
+ *      Portions Copyright 2012-2015 ForgeRock AS.
  */
 package org.forgerock.opendj.ldap.schema;
 
 import java.util.Collections;
-import java.util.HashSet;
-import java.util.Set;
 
 import org.forgerock.i18n.LocalizedIllegalArgumentException;
 import org.forgerock.opendj.ldap.ByteString;
@@ -46,6 +44,7 @@
 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.*;
@@ -1151,18 +1150,15 @@
      */
     @Test
     public final void testSchemaBuilderAddObjectClass() throws Exception {
-        final SchemaBuilder scBuild = new SchemaBuilder(Schema.getDefaultSchema());
-        Set<String> attrs = new HashSet<String>();
-        attrs.add("seeAlso");
-        attrs.add("ou");
-        attrs.add("l");
-        attrs.add("description");
-
-        scBuild.addObjectClass("2.5.6.14", Collections.singletonList("device"),
-                "New description for the new existing Object Class", false, Collections
-                        .singleton(TOP_OBJECTCLASS_NAME), Collections.singleton("cn"), attrs,
-                ObjectClassType.STRUCTURAL, Collections.singletonMap(SCHEMA_PROPERTY_ORIGIN,
-                        Collections.singletonList("RFC 4519")), true);
+        final SchemaBuilder scBuild = new SchemaBuilder(getDefaultSchema());
+        scBuild.buildObjectClass("2.5.6.14")
+                .names("device")
+                .description("New description for the new existing Object Class")
+                .superiorObjectClasses(TOP_OBJECTCLASS_NAME)
+                .requiredAttributes("cn")
+                .optionalAttributes("seeAlso", "ou", "l", "description")
+                .extraProperties(SCHEMA_PROPERTY_ORIGIN, "RFC 4519")
+                .addToSchemaOverwrite();
         Schema sc = scBuild.toSchema();
 
         assertThat(sc.getWarnings()).isEmpty();

--
Gitblit v1.10.0