From 57d38c450aab6379ce7c08d58757cdddba7ef1ab Mon Sep 17 00:00:00 2001
From: matthew_swift <matthew_swift@localhost>
Date: Mon, 02 Oct 2006 09:39:35 +0000
Subject: [PATCH] Add test suite for org.opends.server.types.ObjectClass and perform the following refactoring:

---
 opends/src/server/org/opends/server/core/DirectoryServer.java                                   |   66 
 opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java                             |    3 
 opends/src/server/org/opends/server/types/ObjectClass.java                                      | 1144 ++----------
 opends/src/server/org/opends/server/types/AttributeType.java                                    |  509 -----
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java        |  796 +-------
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestCommonSchemaElements.java |  789 +++++++++
 opends/src/server/org/opends/server/core/SchemaConfigManager.java                               |    2 
 opends/src/server/org/opends/server/types/CommonSchemaElements.java                             |  562 ++++++
 opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java          | 1237 ++++++++++++++
 opends/src/server/org/opends/server/core/Schema.java                                            |    6 
 opends/src/server/org/opends/server/schema/ObjectClassSyntax.java                               |   37 
 11 files changed, 3,043 insertions(+), 2,108 deletions(-)

diff --git a/opends/src/server/org/opends/server/core/DirectoryServer.java b/opends/src/server/org/opends/server/core/DirectoryServer.java
index 4dfa929..de68210 100644
--- a/opends/src/server/org/opends/server/core/DirectoryServer.java
+++ b/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -2867,24 +2867,10 @@
          directoryServer.schema.getObjectClass(TOP_OBJECTCLASS_NAME);
     if (objectClass == null)
     {
-      ConcurrentHashMap<String,String> names =
-           new ConcurrentHashMap<String,String>(1);
-      names.put(TOP_OBJECTCLASS_NAME, TOP_OBJECTCLASS_NAME);
-
-      CopyOnWriteArraySet<AttributeType> requiredAttrs =
-           new CopyOnWriteArraySet<AttributeType>();
-      CopyOnWriteArraySet<AttributeType> optionalAttrs =
-           new CopyOnWriteArraySet<AttributeType>();
-
-      ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
-           new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>(0);
-
-      objectClass = new ObjectClass(TOP_OBJECTCLASS_NAME, names,
-                                    TOP_OBJECTCLASS_OID,
-                                    TOP_OBJECTCLASS_DESCRIPTION, null,
-                                    requiredAttrs, optionalAttrs,
-                                    ObjectClassType.ABSTRACT, false,
-                                    extraProperties);
+      objectClass = new ObjectClass(TOP_OBJECTCLASS_NAME, Collections
+          .singleton(TOP_OBJECTCLASS_NAME), TOP_OBJECTCLASS_OID,
+          TOP_OBJECTCLASS_DESCRIPTION, null, null, null,
+          ObjectClassType.ABSTRACT, false, null);
     }
 
     return objectClass;
@@ -2893,14 +2879,16 @@
 
 
   /**
-   * Causes the Directory Server to construct a new objectclass definition with
-   * the provided name and with no required or allowed attributes.  This should
-   * only be used if there is no objectclass for the specified name.  It will
-   * not register the created objectclass with the Directory Server.
+   * Causes the Directory Server to construct a new objectclass
+   * definition with the provided name and with no required or allowed
+   * attributes. This should only be used if there is no objectclass
+   * for the specified name. It will not register the created
+   * objectclass with the Directory Server.
    *
-   * @param  name  The name to use for the objectclass, as provided by the user.
-   *
-   * @return  The constructed objectclass definition.
+   * @param name
+   *          The name to use for the objectclass, as provided by the
+   *          user.
+   * @return The constructed objectclass definition.
    */
   public static ObjectClass getDefaultObjectClass(String name)
   {
@@ -2911,22 +2899,10 @@
     ObjectClass objectClass = directoryServer.schema.getObjectClass(lowerName);
     if (objectClass == null)
     {
-      ConcurrentHashMap<String,String> names =
-           new ConcurrentHashMap<String,String>(1);
-      names.put(lowerName, name);
-
-      CopyOnWriteArraySet<AttributeType> requiredAttrs =
-           new CopyOnWriteArraySet<AttributeType>();
-      CopyOnWriteArraySet<AttributeType> optionalAttrs =
-           new CopyOnWriteArraySet<AttributeType>();
-
-      ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
-           new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>(0);
-
-      objectClass = new ObjectClass(name, names, lowerName, null,
-                                    getTopObjectClass(), requiredAttrs,
-                                    optionalAttrs, ObjectClassType.ABSTRACT,
-                                    false, extraProperties);
+      objectClass = new ObjectClass(name,
+          Collections.singleton(name), lowerName, null,
+          getTopObjectClass(), null, null, ObjectClassType.ABSTRACT,
+          false, null);
     }
 
     return objectClass;
@@ -2935,11 +2911,11 @@
 
 
   /**
-   * Retrieves the set of attribute type definitions that have been defined in
-   * the Directory Server.
+   * Retrieves the set of attribute type definitions that have been
+   * defined in the Directory Server.
    *
-   * @return  The set of attribute type definitions that have been defined in
-   *          the Directory Server.
+   * @return The set of attribute type definitions that have been
+   *         defined in the Directory Server.
    */
   public static ConcurrentHashMap<String,AttributeType> getAttributeTypes()
   {
diff --git a/opends/src/server/org/opends/server/core/Schema.java b/opends/src/server/org/opends/server/core/Schema.java
index f38350b..106366b 100644
--- a/opends/src/server/org/opends/server/core/Schema.java
+++ b/opends/src/server/org/opends/server/core/Schema.java
@@ -448,7 +448,7 @@
                                        msgID);
         }
 
-        for (String name : objectClass.getNames().keySet())
+        for (String name : objectClass.getNormalizedNames())
         {
           if (objectClasses.containsKey(name))
           {
@@ -465,7 +465,7 @@
 
       objectClasses.put(toLowerCase(objectClass.getOID()), objectClass);
 
-      for (String name : objectClass.getNames().keySet())
+      for (String name : objectClass.getNormalizedNames())
       {
         objectClasses.put(name, objectClass);
       }
@@ -496,7 +496,7 @@
     {
       objectClasses.remove(toLowerCase(objectClass.getOID()), objectClass);
 
-      for (String name : objectClass.getNames().keySet())
+      for (String name : objectClass.getNormalizedNames())
       {
         objectClasses.remove(name, objectClass);
       }
diff --git a/opends/src/server/org/opends/server/core/SchemaConfigManager.java b/opends/src/server/org/opends/server/core/SchemaConfigManager.java
index 9b48df6..6bf0144 100644
--- a/opends/src/server/org/opends/server/core/SchemaConfigManager.java
+++ b/opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -1005,8 +1005,6 @@
             try
             {
               oc = ocSyntax.decodeObjectClass(v.getValue(), schema);
-              oc.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
-              oc.setSchemaFile(schemaFile);
             }
             catch (DirectoryException de)
             {
diff --git a/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java b/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
index 2ad1981..663c1fa 100644
--- a/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
+++ b/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
@@ -30,6 +30,7 @@
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.LinkedHashMap;
 import java.util.LinkedList;
 import java.util.List;
 import org.opends.server.api.ApproximateMatchingRule;
@@ -491,7 +492,7 @@
     boolean isObsolete = false;
     boolean isSingleValue = false;
     HashMap<String,List<String>> extraProperties =
-         new HashMap<String,List<String>>();
+         new LinkedHashMap<String,List<String>>();
 
 
     while (true)
diff --git a/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java b/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java
index 1b1b4fe..46b7717 100644
--- a/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java
+++ b/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java
@@ -28,10 +28,12 @@
 
 
 
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
 import java.util.LinkedList;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
@@ -477,19 +479,17 @@
     // out what it is and how to treat what comes after it, then repeat until
     // we get to the end of the value.  But before we start, set default values
     // for everything else we might need to know.
-    String  primaryName = oid;
-    ConcurrentHashMap<String,String> names =
-         new ConcurrentHashMap<String,String>();
+    String primaryName = oid;
+    List<String> names = new LinkedList<String>();
     String description = null;
     boolean isObsolete = false;
     ObjectClass superiorClass = DirectoryServer.getTopObjectClass();
-    CopyOnWriteArraySet<AttributeType> requiredAttributes =
-         new CopyOnWriteArraySet<AttributeType>();
-    CopyOnWriteArraySet<AttributeType> optionalAttributes =
-         new CopyOnWriteArraySet<AttributeType>();
-    ObjectClassType objectClassType = superiorClass.getObjectClassType();
-    ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
-         new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>();
+    Set<AttributeType> requiredAttributes = new LinkedHashSet<AttributeType>();
+    Set<AttributeType> optionalAttributes = new LinkedHashSet<AttributeType>();
+    ObjectClassType objectClassType = superiorClass
+        .getObjectClassType();
+    Map<String, List<String>> extraProperties =
+      new LinkedHashMap<String, List<String>>();
 
 
     while (true)
@@ -525,7 +525,7 @@
           pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
                                  (pos-1));
           primaryName = userBuffer.toString();
-          names.put(lowerBuffer.toString(), primaryName);
+          names.add(primaryName);
         }
         else if (c == '(')
         {
@@ -534,7 +534,7 @@
           pos = readQuotedString(valueStr, lowerStr, userBuffer, lowerBuffer,
                                  pos);
           primaryName = userBuffer.toString();
-          names.put(lowerBuffer.toString(), primaryName);
+          names.add(primaryName);
 
 
           while (true)
@@ -557,7 +557,7 @@
 
               pos = readQuotedString(valueStr, lowerStr, userBuffer,
                                      lowerBuffer, pos);
-              names.put(lowerBuffer.toString(), userBuffer.toString());
+              names.add(userBuffer.toString());
             }
           }
         }
@@ -792,8 +792,7 @@
         // either a single value in single quotes or an open parenthesis
         // followed by one or more values in single quotes separated by spaces
         // followed by a close parenthesis.
-        CopyOnWriteArrayList<String> valueList =
-             new CopyOnWriteArrayList<String>();
+        List<String> valueList = new LinkedList<String>();
         pos = readExtraParameterValues(valueStr, valueList, pos);
         extraProperties.put(tokenName, valueList);
       }
@@ -1228,7 +1227,7 @@
    *                              the value.
    */
   private static int readExtraParameterValues(String valueStr,
-                          CopyOnWriteArrayList<String> valueList, int startPos)
+                          List<String> valueList, int startPos)
           throws DirectoryException
   {
     assert debugEnter(CLASS_NAME, "readExtraParameterValues",
diff --git a/opends/src/server/org/opends/server/types/AttributeType.java b/opends/src/server/org/opends/server/types/AttributeType.java
index 1876bee..0d56cab 100644
--- a/opends/src/server/org/opends/server/types/AttributeType.java
+++ b/opends/src/server/org/opends/server/types/AttributeType.java
@@ -29,9 +29,6 @@
 
 
 import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import org.opends.server.api.ApproximateMatchingRule;
@@ -46,7 +43,6 @@
 import static org.opends.server.messages.CoreMessages.*;
 import static org.opends.server.messages.MessageHandler.*;
 import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
 
 
 
@@ -55,8 +51,17 @@
  * with an attribute type, which contains information about the format
  * of an attribute and the syntax and matching rules that should be
  * used when interacting with it.
+ * <p>
+ * Any methods which accesses the set of names associated with this
+ * attribute type, will retrieve the primary name as the first name,
+ * regardless of whether or not it was contained in the original set
+ * of <code>names</code> passed to the constructor.
+ * <p>
+ * Where ordered sets of names, or extra properties are provided, the
+ * ordering will be preserved when the associated fields are accessed
+ * via their getters or via the {@link #toString()} methods.
  */
-public final class AttributeType
+public final class AttributeType extends CommonSchemaElements
 {
   /**
    * The fully-qualified name of this class for debugging purposes.
@@ -89,38 +94,15 @@
   // Indicates whether this attribute type is the objectclass type.
   private final boolean isObjectClassType;
 
-  // Indicates whether this attribute type is declared "obsolete".
-  private final boolean isObsolete;
-
   // Indicates whether this attribute type is declared "single-value".
   private final boolean isSingleValue;
 
-  // The set of additional name-value pairs associated with this
-  // attribute type definition.
-  private final Map<String,List<String>> extraProperties;
-
-  // The set of names for this attribute type, in a mapping between
-  // the all-lowercase form and the user-defined form.
-  private final Map<String,String> typeNames;
-
   // The equality matching rule for this attribute type.
   private final EqualityMatchingRule equalityMatchingRule;
 
   // The ordering matching rule for this attribute type.
   private final OrderingMatchingRule orderingMatchingRule;
 
-  // The description for this attribute type.
-  private final String description;
-
-  // The OID that may be used to reference this attribute type.
-  private final String oid;
-
-  // The primary name to use for this attribute type.
-  private final String primaryName;
-
-  // The lower case name for this attribute type.
-  private final String lowerName;
-
   // The substring matching rule for this attribute type.
   private final SubstringMatchingRule substringMatchingRule;
 
@@ -128,6 +110,11 @@
 
   /**
    * Creates a new attribute type with the provided information.
+   * <p>
+   * If no <code>primaryName</code> is specified, but a set of
+   * <code>names</code> is specified, then the first name retrieved
+   * from the set of <code>names</code> will be used as the primary
+   * name.
    *
    * @param primaryName
    *          The primary name for this attribute type, or
@@ -184,6 +171,11 @@
 
   /**
    * Creates a new attribute type with the provided information.
+   * <p>
+   * If no <code>primaryName</code> is specified, but a set of
+   * <code>names</code> is specified, then the first name retrieved
+   * from the set of <code>names</code> will be used as the primary
+   * name.
    *
    * @param primaryName
    *          The primary name for this attribute type, or
@@ -234,6 +226,8 @@
    * @param extraProperties
    *          A set of extra properties for this attribute type, or
    *          <code>null</code> if there are no extra properties.
+   * @throws NullPointerException
+   *           If the provided OID was <code>null</code>.
    */
   public AttributeType(String primaryName,
                        Collection<String> typeNames,
@@ -250,7 +244,11 @@
                        boolean isNoUserModification,
                        boolean isObsolete, boolean isSingleValue,
                        Map<String,List<String>> extraProperties)
+                       throws NullPointerException
   {
+    super(primaryName, typeNames, oid, description, isObsolete,
+        extraProperties);
+
     assert debugConstructor(CLASS_NAME,String.valueOf(primaryName),
                               String.valueOf(typeNames),
                               String.valueOf(oid),
@@ -268,43 +266,11 @@
                               String.valueOf(isSingleValue),
                               String.valueOf(extraProperties));
 
-    // Make sure mandatory parameters are specified.
-    if (oid == null)
-    {
-      throw new NullPointerException(
-          "No oid specified in constructor");
-    }
-
-    this.primaryName = primaryName;
-    this.lowerName = toLowerCase(primaryName);
-    this.oid = oid;
-    this.description = description;
     this.superiorType = superiorType;
     this.isCollective = isCollective;
     this.isNoUserModification = isNoUserModification;
-    this.isObsolete = isObsolete;
     this.isSingleValue = isSingleValue;
 
-    // Construct the normalized attribute name mapping.
-    if (typeNames != null)
-    {
-      this.typeNames = new HashMap<String, String>(typeNames.size());
-      for (String name : typeNames)
-      {
-        this.typeNames.put(toLowerCase(name), name);
-      }
-    }
-    else
-    {
-      this.typeNames = new HashMap<String, String>();
-    }
-
-    // Add the primary name to the type names if it is not present.
-    if (lowerName != null && !this.typeNames.containsKey(lowerName))
-    {
-      this.typeNames.put(lowerName, this.primaryName);
-    }
-
     if (syntax == null)
     {
       if (superiorType != null)
@@ -380,208 +346,8 @@
     }
     else
     {
-      isObjectClassType =
-        this.typeNames.containsKey(OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
+      isObjectClassType = hasName(OBJECTCLASS_ATTRIBUTE_TYPE_NAME);
     }
-
-    if (extraProperties != null)
-    {
-      this.extraProperties =
-        new HashMap<String, List<String>>(extraProperties);
-    }
-    else
-    {
-      this.extraProperties = Collections.emptyMap();
-    }
-  }
-
-
-
-  /**
-   * Retrieves the primary name for this attribute type.
-   *
-   * @return The primary name for this attribute type, or
-   *         <code>null</code> if there is no primary name.
-   */
-  public String getPrimaryName()
-  {
-    assert debugEnter(CLASS_NAME, "getPrimaryName");
-
-    return primaryName;
-  }
-
-
-
-  /**
-   * Retrieve the normalized primary name for this attribute type.
-   *
-   * @return Returns the normalized primary name for this attribute
-   *         type, or <code>null</code> if there is no primary name.
-   */
-  public String getNormalizedPrimaryName()
-  {
-    assert debugEnter(CLASS_NAME, "getNormalizedPrimaryName");
-
-    return lowerName;
-  }
-
-
-
-  /**
-   * Retrieves an iterable over the set of normalized names that may
-   * be used to reference this attribute type. The normalized form of
-   * an attribute name is defined as the user-defined name converted
-   * to lower-case.
-   *
-   * @return Returns an iterable over the set of normalized names that
-   *         may be used to reference this attribute type.
-   */
-  public Iterable<String> getNormalizedNames()
-  {
-    assert debugEnter(CLASS_NAME, "getNormalizedNames");
-
-    return typeNames.keySet();
-  }
-
-
-
-  /**
-   * Retrieves an iterable over the set of user-defined names that may
-   * be used to reference this attribute type.
-   *
-   * @return Returns an iterable over the set of user-defined names
-   *         that may be used to reference this attribute type.
-   */
-  public Iterable<String> getUserDefinedNames()
-  {
-    assert debugEnter(CLASS_NAME, "getUserDefinedNames");
-
-    return typeNames.values();
-  }
-
-
-
-  /**
-   * Indicates whether this attribute type has the specified name.
-   *
-   * @param  lowerName  The lowercase name for which to make the
-   *                    determination.
-   *
-   * @return  <CODE>true</CODE> if the specified name is assigned to
-   *          this attribute type, or <CODE>false</CODE> if not.
-   */
-  public boolean hasName(String lowerName)
-  {
-    assert debugEnter(CLASS_NAME, "hasName",
-                      String.valueOf(lowerName));
-
-    return typeNames.containsKey(lowerName);
-  }
-
-
-
-  /**
-   * Retrieves the OID for this attribute type.
-   *
-   * @return  The OID for this attribute type.
-   */
-  public String getOID()
-  {
-    assert debugEnter(CLASS_NAME, "getOID");
-
-    return oid;
-  }
-
-
-
-  /**
-   * Retrieves the name or OID for this attribute type.  If it has one
-   * or more names, then the primary name will be returned.  If it
-   * does not have any names, then the OID will be returned.
-   *
-   * @return  The name or OID for this attribute type.
-   */
-  public String getNameOrOID()
-  {
-    assert debugEnter(CLASS_NAME, "getNameOrOID");
-
-    if (primaryName != null)
-    {
-      return primaryName;
-    }
-
-    if (typeNames.isEmpty())
-    {
-      return oid;
-    }
-    else
-    {
-      return typeNames.values().iterator().next();
-    }
-  }
-
-
-
-  /**
-   * Indicates whether this attribute type has the specified name or
-   * OID.
-   *
-   * @param  lowerValue  The lowercase value for which to make the
-   *                     determination.
-   *
-   * @return  <CODE>true</CODE> if the provided value matches the OID
-   *          or one of the names assigned to this attribute type, or
-   *          <CODE>false</CODE> if not.
-   */
-  public boolean hasNameOrOID(String lowerValue)
-  {
-    assert debugEnter(CLASS_NAME, "hasNameOrOID",
-                      String.valueOf(lowerValue));
-
-    if (typeNames.containsKey(lowerValue))
-    {
-      return true;
-    }
-
-    return oid.equals(lowerValue);
-  }
-
-
-
-  /**
-   * Retrieves the path to the schema file that contains the
-   * definition for this attribute type.
-   *
-   * @return  The path to the schema file that contains the definition
-   *          for this attribute type, or <CODE>null</CODE> if it is
-   *          not known or if it is not stored in any schema file.
-   */
-  public String getSchemaFile()
-  {
-    assert debugEnter(CLASS_NAME, "getSchemaFile");
-
-    List<String> values =
-      extraProperties.get(SCHEMA_PROPERTY_FILENAME);
-    if (values != null && !values.isEmpty()) {
-      return values.get(0);
-    }
-
-    return null;
-  }
-
-
-
-  /**
-   * Retrieves the description for this attribute type.
-   *
-   * @return  The description for this attribute type, or
-   *         <code>null</code> if there is no description.
-   */
-  public String getDescription()
-  {
-    assert debugEnter(CLASS_NAME, "getDescription");
-
-    return description;
   }
 
 
@@ -759,21 +525,6 @@
 
 
   /**
-   * Indicates whether this attribute type is declared "obsolete".
-   *
-   * @return  <CODE>true</CODE> if this attribute type is declared
-   *          "obsolete", or <CODE>false</CODE> if not.
-   */
-  public boolean isObsolete()
-  {
-    assert debugEnter(CLASS_NAME, "isObsolete");
-
-    return isObsolete;
-  }
-
-
-
-  /**
    * Indicates whether this attribute type is declared "single-value".
    *
    * @return  <CODE>true</CODE> if this attribute type is declared
@@ -789,43 +540,6 @@
 
 
   /**
-   * Retrieves an iterable over the names of "extra" properties
-   * associated with this attribute type.
-   *
-   * @return Returns an iterable over the names of "extra" properties
-   *         associated with this attribute type.
-   */
-  public Iterable<String> getExtraPropertyNames()
-  {
-    assert debugEnter(CLASS_NAME, "getExtraPropertyNames");
-
-    return extraProperties.keySet();
-  }
-
-
-
-  /**
-   * Retrieves an iterable over the value(s) of the specified "extra"
-   * property for this attribute type.
-   *
-   * @param propertyName
-   *          The name of the "extra" property for which to retrieve
-   *          the value(s).
-   * @return Returns an iterable over the value(s) of the specified
-   *         "extra" property for this attribute type, or
-   *         <CODE>null</CODE> if no such property is defined.
-   */
-  public Iterable<String> getExtraProperty(String propertyName)
-  {
-    assert debugEnter(CLASS_NAME, "getExtraProperty",
-                      String.valueOf(propertyName));
-
-    return extraProperties.get(propertyName);
-  }
-
-
-
-  /**
    * Indicates whether this attribute type represents the
    * "objectclass" attribute.  The determination will be made based on
    * the name and/or OID.
@@ -875,57 +589,6 @@
 
 
   /**
-   * Indicates whether the provided object is equal to this attribute
-   * type.  The object will be considered equal if it is an attribute
-   * type with the same OID as the current type.
-   *
-   * @param  o  The object for which to make the determination.
-   *
-   * @return  <CODE>true</CODE> if the provided object is equal to
-   *          this attribute type, or <CODE>false</CODE> if not.
-   */
-  public boolean equals(Object o)
-  {
-    assert debugEnter(CLASS_NAME, "equals");
-
-    if (this == o)
-    {
-      return true;
-    }
-
-    if ((o == null) || (! (o instanceof AttributeType)))
-    {
-      return false;
-    }
-
-    return oid.equals(((AttributeType) o).oid);
-  }
-
-
-
-  /**
-   * Retrieves the hash code for this attribute type.  It will be
-   * based on the sum of the bytes of the OID.
-   *
-   * @return  The hash code for this attribute type.
-   */
-  public int hashCode()
-  {
-    assert debugEnter(CLASS_NAME, "hashCode");
-
-    int oidLength = oid.length();
-    int hashCode  = 0;
-    for (int i=0; i < oidLength; i++)
-    {
-      hashCode += oid.charAt(i);
-    }
-
-    return hashCode;
-  }
-
-
-
-  /**
    * Generates a hash code for the specified attribute value.  If an
    * equality matching rule is defined for this type, then it will be
    * used to generate the hash code.  If the value does not have an
@@ -982,81 +645,12 @@
 
 
   /**
-   * Retrieves the string representation of this attribute type in the
-   * form specified in RFC 2252.
-   *
-   * @return  The string representation of this attribute type in the
-   *          form specified in RFC 2252.
+   * {@inheritDoc}
    */
-  public String toString()
+  protected void toStringContent(StringBuilder buffer)
   {
-    assert debugEnter(CLASS_NAME, "toString");
-
-    StringBuilder buffer = new StringBuilder();
-    toString(buffer, true);
-    return buffer.toString();
-  }
-
-
-
-  /**
-   * Appends a string representation of this attribute type in the
-   * form specified in RFC 2252 to the provided buffer.
-   *
-   * @param  buffer              The buffer to which the information
-   *                             should be appended.
-   * @param  includeFileElement  Indicates whether to include an
-   *                             "extra" property that specifies the
-   *                             path to the schema file from which
-   *                             this attribute type was loaded.
-   */
-  public void toString(StringBuilder buffer,
-                       boolean includeFileElement)
-  {
-    assert debugEnter(CLASS_NAME, "toString",
-                      "java.lang.StringBuilder",
-                      String.valueOf(includeFileElement));
-
-    buffer.append("( ");
-    buffer.append(oid);
-
-    if (! typeNames.isEmpty())
-    {
-      Iterator<String> iterator = typeNames.values().iterator();
-
-      String firstName = iterator.next();
-      if (iterator.hasNext())
-      {
-        buffer.append(" NAME ( '");
-        buffer.append(firstName);
-
-        while (iterator.hasNext())
-        {
-          buffer.append("' '");
-          buffer.append(iterator.next());
-        }
-
-        buffer.append("' )");
-      }
-      else
-      {
-        buffer.append(" NAME '");
-        buffer.append(firstName);
-        buffer.append("'");
-      }
-    }
-
-    if ((description != null) && (description.length() > 0))
-    {
-      buffer.append(" DESC '");
-      buffer.append(description);
-      buffer.append("'");
-    }
-
-    if (isObsolete)
-    {
-      buffer.append(" OBSOLETE");
-    }
+    assert debugEnter(CLASS_NAME, "toStringContent",
+                      "java.lang.StringBuilder");
 
     if (superiorType != null)
     {
@@ -1114,47 +708,6 @@
       buffer.append(" USAGE ");
       buffer.append(attributeUsage.toString());
     }
-
-    if (! extraProperties.isEmpty())
-    {
-      for (Map.Entry<String, List<String>> e :
-        extraProperties.entrySet()) {
-
-        String property = e.getKey();
-        if (!includeFileElement
-            && property.equals(SCHEMA_PROPERTY_FILENAME)) {
-          // Don't include the schema file if it was not requested.
-          continue;
-        }
-
-        List<String> valueList = e.getValue();
-
-        buffer.append(" ");
-        buffer.append(property);
-
-        if (valueList.size() == 1)
-        {
-          buffer.append(" '");
-          buffer.append(valueList.get(0));
-          buffer.append("'");
-        }
-        else
-        {
-          buffer.append(" ( ");
-
-          for (String value : valueList)
-          {
-            buffer.append("'");
-            buffer.append(value);
-            buffer.append("' ");
-          }
-
-          buffer.append(")");
-        }
-      }
-    }
-
-    buffer.append(" )");
   }
 }
 
diff --git a/opends/src/server/org/opends/server/types/CommonSchemaElements.java b/opends/src/server/org/opends/server/types/CommonSchemaElements.java
new file mode 100644
index 0000000..8304121
--- /dev/null
+++ b/opends/src/server/org/opends/server/types/CommonSchemaElements.java
@@ -0,0 +1,562 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import static org.opends.server.loggers.Debug.debugConstructor;
+import static org.opends.server.loggers.Debug.debugEnter;
+import static org.opends.server.util.ServerConstants.*;
+import static org.opends.server.util.StaticUtils.toLowerCase;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+
+
+/**
+ * An abstract base class for LDAP schema definitions which contain an
+ * OID, optional names, description, an obsolete flag, and an optional
+ * set of extra properties.
+ * <p>
+ * This class defines common properties and behaviour of the various
+ * types of schema definitions (e.g. object class definitions, and
+ * attribute type definitions).
+ * <p>
+ * Any methods which accesses the set of names associated with this
+ * definition, will retrieve the primary name as the first name,
+ * regardless of whether or not it was contained in the original set
+ * of <code>names</code> passed to the constructor.
+ * <p>
+ * Where ordered sets of names, or extra properties are provided, the
+ * ordering will be preserved when the associated fields are accessed
+ * via their getters or via the {@link #toString()} methods.
+ */
+public abstract class CommonSchemaElements {
+  /**
+   * The fully-qualified name of this class for debugging purposes.
+   */
+  private static final String CLASS_NAME =
+    "org.opends.server.types.CommonSchemaElements";
+
+  // Indicates whether this definition is declared "obsolete".
+  private final boolean isObsolete;
+
+  // The set of additional name-value pairs associated with this
+  // definition.
+  private final Map<String, List<String>> extraProperties;
+
+  // The set of names for this definition, in a mapping between
+  // the all-lowercase form and the user-defined form.
+  private final Map<String, String> names;
+
+  // The description for this definition.
+  private final String description;
+
+  // The OID that may be used to reference this definition.
+  private final String oid;
+
+  // The primary name to use for this definition.
+  private final String primaryName;
+
+  // The lower case name for this definition.
+  private final String lowerName;
+
+
+
+  /**
+   * Creates a new definition with the provided information.
+   * <p>
+   * If no <code>primaryName</code> is specified, but a set of
+   * <code>names</code> is specified, then the first name retrieved
+   * from the set of <code>names</code> will be used as the primary
+   * name.
+   *
+   * @param primaryName
+   *          The primary name for this definition, or
+   *          <code>null</code> if there is no primary name.
+   * @param names
+   *          The full set of names for this definition, or
+   *          <code>null</code> if there are no names.
+   * @param oid
+   *          The OID for this definition (must not be
+   *          <code>null</code>).
+   * @param description
+   *          The description for the definition, or <code>null</code>
+   *          if there is no description.
+   * @param isObsolete
+   *          Indicates whether this definition is declared
+   *          "obsolete".
+   * @param extraProperties
+   *          A set of extra properties for this definition, or
+   *          <code>null</code> if there are no extra properties.
+   * @throws NullPointerException
+   *           If the provided OID was <code>null</code>.
+   */
+  protected CommonSchemaElements(String primaryName,
+      Collection<String> names, String oid, String description,
+      boolean isObsolete, Map<String, List<String>> extraProperties)
+      throws NullPointerException {
+
+    assert debugConstructor(CLASS_NAME, String.valueOf(primaryName),
+        String.valueOf(names), String.valueOf(oid), String
+            .valueOf(description), String.valueOf(isObsolete), String
+            .valueOf(extraProperties));
+
+    // Make sure mandatory parameters are specified.
+    if (oid == null) {
+      throw new NullPointerException(
+          "No oid specified in constructor");
+    }
+
+    this.oid = oid;
+    this.description = description;
+    this.isObsolete = isObsolete;
+
+    // Make sure we have a primary name if possible.
+    if (primaryName == null) {
+      if (names != null && !names.isEmpty()) {
+        this.primaryName = names.iterator().next();
+      } else {
+        this.primaryName = null;
+      }
+    } else {
+      this.primaryName = primaryName;
+    }
+    this.lowerName = toLowerCase(primaryName);
+
+    // Construct the normalized attribute name mapping.
+    if (names != null) {
+      this.names = new LinkedHashMap<String, String>(names.size());
+
+      // Make sure the primary name is first (never null).
+      this.names.put(lowerName, this.primaryName);
+
+      // Add the remaining names in the order specified.
+      for (String name : names) {
+        this.names.put(toLowerCase(name), name);
+      }
+    } else if (this.primaryName != null) {
+      this.names = Collections.singletonMap(lowerName,
+          this.primaryName);
+    } else {
+      this.names = Collections.emptyMap();
+    }
+
+    // FIXME: should really be a deep-copy.
+    if (extraProperties != null) {
+      this.extraProperties = new LinkedHashMap<String, List<String>>(
+          extraProperties);
+    } else {
+      this.extraProperties = Collections.emptyMap();
+    }
+  }
+
+
+
+  /**
+   * Retrieves the primary name for this schema definition.
+   *
+   * @return The primary name for this schema definition, or
+   *         <code>null</code> if there is no primary name.
+   */
+  public final String getPrimaryName() {
+    assert debugEnter(CLASS_NAME, "getPrimaryName");
+
+    return primaryName;
+  }
+
+
+
+  /**
+   * Retrieve the normalized primary name for this schema definition.
+   *
+   * @return Returns the normalized primary name for this attribute
+   *         type, or <code>null</code> if there is no primary name.
+   */
+  public final String getNormalizedPrimaryName() {
+    assert debugEnter(CLASS_NAME, "getNormalizedPrimaryName");
+
+    return lowerName;
+  }
+
+
+
+  /**
+   * Retrieves an iterable over the set of normalized names that may
+   * be used to reference this schema definition. The normalized form
+   * of an attribute name is defined as the user-defined name
+   * converted to lower-case.
+   *
+   * @return Returns an iterable over the set of normalized names that
+   *         may be used to reference this schema definition.
+   */
+  public final Iterable<String> getNormalizedNames() {
+    assert debugEnter(CLASS_NAME, "getNormalizedNames");
+
+    return names.keySet();
+  }
+
+
+
+  /**
+   * Retrieves an iterable over the set of user-defined names that may
+   * be used to reference this schema definition.
+   *
+   * @return Returns an iterable over the set of user-defined names
+   *         that may be used to reference this schema definition.
+   */
+  public final Iterable<String> getUserDefinedNames() {
+    assert debugEnter(CLASS_NAME, "getUserDefinedNames");
+
+    return names.values();
+  }
+
+
+
+  /**
+   * Indicates whether this schema definition has the specified name.
+   *
+   * @param lowerName
+   *          The lowercase name for which to make the determination.
+   * @return <code>true</code> if the specified name is assigned to
+   *         this schema definition, or <code>false</code> if not.
+   */
+  public final boolean hasName(String lowerName) {
+    assert debugEnter(CLASS_NAME, "hasName", String
+        .valueOf(lowerName));
+
+    return names.containsKey(lowerName);
+  }
+
+
+
+  /**
+   * Retrieves the OID for this schema definition.
+   *
+   * @return The OID for this schema definition.
+   */
+  public final String getOID() {
+    assert debugEnter(CLASS_NAME, "getOID");
+
+    return oid;
+  }
+
+
+
+  /**
+   * Retrieves the name or OID for this schema definition. If it has
+   * one or more names, then the primary name will be returned. If it
+   * does not have any names, then the OID will be returned.
+   *
+   * @return The name or OID for this schema definition.
+   */
+  public final String getNameOrOID() {
+    assert debugEnter(CLASS_NAME, "getNameOrOID");
+
+    if (primaryName != null) {
+      return primaryName;
+    } else {
+      // Guaranteed not to be null.
+      return oid;
+    }
+  }
+
+
+
+  /**
+   * Indicates whether this schema definition has the specified name
+   * or OID.
+   *
+   * @param lowerValue
+   *          The lowercase value for which to make the determination.
+   * @return <code>true</code> if the provided value matches the OID
+   *         or one of the names assigned to this schema definition,
+   *         or <code>false</code> if not.
+   */
+  public final boolean hasNameOrOID(String lowerValue) {
+    assert debugEnter(CLASS_NAME, "hasNameOrOID", String
+        .valueOf(lowerValue));
+
+    if (names.containsKey(lowerValue)) {
+      return true;
+    }
+
+    return oid.equals(lowerValue);
+  }
+
+
+
+  /**
+   * Retrieves the path to the schema file that contains the
+   * definition for this schema definition.
+   *
+   * @return The path to the schema file that contains the definition
+   *         for this schema definition, or <code>null</code> if it
+   *         is not known or if it is not stored in any schema file.
+   */
+  public final String getSchemaFile() {
+    assert debugEnter(CLASS_NAME, "getSchemaFile");
+
+    List<String> values = extraProperties
+        .get(SCHEMA_PROPERTY_FILENAME);
+    if (values != null && !values.isEmpty()) {
+      return values.get(0);
+    }
+
+    return null;
+  }
+
+
+
+  /**
+   * Retrieves the description for this schema definition.
+   *
+   * @return The description for this schema definition, or
+   *         <code>null</code> if there is no description.
+   */
+  public final String getDescription() {
+    assert debugEnter(CLASS_NAME, "getDescription");
+
+    return description;
+  }
+
+
+
+  /**
+   * Indicates whether this schema definition is declared "obsolete".
+   *
+   * @return <code>true</code> if this schema definition is declared
+   *         "obsolete", or <code>false</code> if not.
+   */
+  public final boolean isObsolete() {
+    assert debugEnter(CLASS_NAME, "isObsolete");
+
+    return isObsolete;
+  }
+
+
+
+  /**
+   * Retrieves an iterable over the names of "extra" properties
+   * associated with this schema definition.
+   *
+   * @return Returns an iterable over the names of "extra" properties
+   *         associated with this schema definition.
+   */
+  public final Iterable<String> getExtraPropertyNames() {
+    assert debugEnter(CLASS_NAME, "getExtraPropertyNames");
+
+    return extraProperties.keySet();
+  }
+
+
+
+  /**
+   * Retrieves an iterable over the value(s) of the specified "extra"
+   * property for this schema definition.
+   *
+   * @param name
+   *          The name of the "extra" property for which to retrieve
+   *          the value(s).
+   * @return Returns an iterable over the value(s) of the specified
+   *         "extra" property for this schema definition, or
+   *         <code>null</code> if no such property is defined.
+   */
+  public final Iterable<String> getExtraProperty(String name) {
+    assert debugEnter(CLASS_NAME, "getExtraProperty", String
+        .valueOf(name));
+
+    return extraProperties.get(name);
+  }
+
+
+
+  /**
+   * Indicates whether the provided object is equal to this attribute
+   * type. The object will be considered equal if it is an attribute
+   * type with the same OID as the current type.
+   *
+   * @param o
+   *          The object for which to make the determination.
+   * @return <code>true</code> if the provided object is equal to
+   *         this schema definition, or <code>false</code> if not.
+   */
+  public final boolean equals(Object o) {
+    assert debugEnter(CLASS_NAME, "equals");
+
+    if (this == o) {
+      return true;
+    }
+
+    if (o instanceof CommonSchemaElements) {
+      CommonSchemaElements other = (CommonSchemaElements) o;
+      return oid.equals(other.oid);
+    }
+
+    return false;
+  }
+
+
+
+  /**
+   * Retrieves the hash code for this schema definition. It will be
+   * based on the sum of the bytes of the OID.
+   *
+   * @return The hash code for this schema definition.
+   */
+  public final int hashCode() {
+    assert debugEnter(CLASS_NAME, "hashCode");
+
+    return oid.hashCode();
+  }
+
+
+
+  /**
+   * Retrieves the string representation of this schema definition in
+   * the form specified in RFC 2252.
+   *
+   * @return The string representation of this schema definition in
+   *         the form specified in RFC 2252.
+   */
+  public final String toString() {
+    assert debugEnter(CLASS_NAME, "toString");
+
+    StringBuilder buffer = new StringBuilder();
+    toString(buffer, true);
+    return buffer.toString();
+  }
+
+
+
+  /**
+   * Appends a string representation of this schema definition in the
+   * form specified in RFC 2252 to the provided buffer.
+   *
+   * @param buffer
+   *          The buffer to which the information should be appended.
+   * @param includeFileElement
+   *          Indicates whether to include an "extra" property that
+   *          specifies the path to the schema file from which this
+   *          schema definition was loaded.
+   */
+  public final void toString(StringBuilder buffer,
+      boolean includeFileElement) {
+    assert debugEnter(CLASS_NAME, "toString",
+        "java.lang.StringBuilder",
+        String.valueOf(includeFileElement));
+
+    buffer.append("( ");
+    buffer.append(oid);
+
+    if (!names.isEmpty()) {
+      Iterator<String> iterator = names.values().iterator();
+
+      String firstName = iterator.next();
+      if (iterator.hasNext()) {
+        buffer.append(" NAME ( '");
+        buffer.append(firstName);
+
+        while (iterator.hasNext()) {
+          buffer.append("' '");
+          buffer.append(iterator.next());
+        }
+
+        buffer.append("' )");
+      } else {
+        buffer.append(" NAME '");
+        buffer.append(firstName);
+        buffer.append("'");
+      }
+    }
+
+    if ((description != null) && (description.length() > 0)) {
+      buffer.append(" DESC '");
+      buffer.append(description);
+      buffer.append("'");
+    }
+
+    if (isObsolete) {
+      buffer.append(" OBSOLETE");
+    }
+
+    // Delegate remaining string output to sub-class.
+    toStringContent(buffer);
+
+    if (!extraProperties.isEmpty()) {
+      for (Map.Entry<String, List<String>> e : extraProperties
+          .entrySet()) {
+
+        String property = e.getKey();
+        if (!includeFileElement
+            && property.equals(SCHEMA_PROPERTY_FILENAME)) {
+          // Don't include the schema file if it was not requested.
+          continue;
+        }
+
+        List<String> valueList = e.getValue();
+
+        buffer.append(" ");
+        buffer.append(property);
+
+        if (valueList.size() == 1) {
+          buffer.append(" '");
+          buffer.append(valueList.get(0));
+          buffer.append("'");
+        } else {
+          buffer.append(" ( ");
+
+          for (String value : valueList) {
+            buffer.append("'");
+            buffer.append(value);
+            buffer.append("' ");
+          }
+
+          buffer.append(")");
+        }
+      }
+    }
+
+    buffer.append(" )");
+  }
+
+
+
+  /**
+   * Appends a string representation of this schema definition's
+   * non-generic properties to the provided buffer.
+   *
+   * @param buffer
+   *          The buffer to which the information should be appended.
+   */
+  protected abstract void toStringContent(StringBuilder buffer);
+}
diff --git a/opends/src/server/org/opends/server/types/ObjectClass.java b/opends/src/server/org/opends/server/types/ObjectClass.java
index 4a496ba..8079522 100644
--- a/opends/src/server/org/opends/server/types/ObjectClass.java
+++ b/opends/src/server/org/opends/server/types/ObjectClass.java
@@ -28,16 +28,18 @@
 
 
 
+import static org.opends.server.loggers.Debug.debugConstructor;
+import static org.opends.server.loggers.Debug.debugEnter;
+import static org.opends.server.util.ServerConstants.*;
+
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashSet;
 import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-import static org.opends.server.loggers.Debug.*;
-import static org.opends.server.util.ServerConstants.*;
-import static org.opends.server.util.StaticUtils.*;
 
 
 
@@ -45,393 +47,168 @@
  * This class defines a data structure for storing and interacting
  * with an objectclass, which contains a collection of attributes that
  * must and/or may be present in an entry with that objectclass.
+ * <p>
+ * Any methods which accesses the set of names associated with this
+ * object class, will retrieve the primary name as the first name,
+ * regardless of whether or not it was contained in the original set
+ * of <code>names</code> passed to the constructor.
+ * <p>
+ * Where ordered sets of names, attribute types, or extra properties
+ * are provided, the ordering will be preserved when the associated
+ * fields are accessed via their getters or via the
+ * {@link #toString()} methods.
  */
-public class ObjectClass
-{
+public final class ObjectClass extends CommonSchemaElements {
   /**
    * The fully-qualified name of this class for debugging purposes.
    */
   private static final String CLASS_NAME =
-       "org.opends.server.types.ObjectClass";
-
-
-
-  // Indicates whether this objectclass is declared "obsolete".
-  private boolean isObsolete;
-
-  // The set of additional name-value pairs associated with this
-  // objectclass definition.
-  private ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
-               extraProperties;
-
-  // The mapping between the lowercase names and the user-provided
-  // names for this objectclass.
-  private ConcurrentHashMap<String,String> names;
+    "org.opends.server.types.ObjectClass";
 
   // The set of optional attribute types for this objectclass.
-  private CopyOnWriteArraySet<AttributeType> optionalAttributes;
+  private final Set<AttributeType> optionalAttributes;
+
+  // The set of optional attribute types for this objectclass and its
+  // superclasses.
+  private final Set<AttributeType> optionalAttributesChain;
 
   // The set of required attribute types for this objectclass.
-  private CopyOnWriteArraySet<AttributeType> requiredAttributes;
+  private final Set<AttributeType> requiredAttributes;
+
+  // The set of required attribute types for this objectclass and its
+  // superclasses.
+  private final Set<AttributeType> requiredAttributesChain;
 
   // The reference to the superior objectclass.
-  private ObjectClass superiorClass;
+  private final ObjectClass superiorClass;
 
   // The objectclass type for this objectclass.
-  private ObjectClassType objectClassType;
+  private final ObjectClassType objectClassType;
 
-  // The description for this objectclass.
-  private String description;
-
-  // The OID for this objectclass.
-  private String oid;
-
-  // The primary name for this objectclass.
-  private String primaryName;
-
-  // The path to the schema file that contains this objectclass
-  // definition.
-  private String schemaFile;
+  // Indicates whether or not this object class is allowed to
+  // contain any attribute.
+  private final boolean isExtensibleObject;
 
 
 
   /**
    * Creates a new objectclass definition with the provided
    * information.
+   * <p>
+   * If no <code>primaryName</code> is specified, but a set of
+   * <code>names</code> is specified, then the first name retrieved
+   * from the set of <code>names</code> will be used as the primary
+   * name.
    *
-   * @param  primaryName         The primary name for this
-   *                             objectclass.
-   * @param  names               The set of names that may be used to
-   *                             reference this objectclass.
-   * @param  oid                 The OID for this objectclass.
-   * @param  description         The description for this objectclass.
-   * @param  superiorClass       The superior class for this
-   *                             objectclass.
-   * @param  requiredAttributes  The set of required attribute types
-   *                             for this objectclass.
-   * @param  optionalAttributes  The set of optional attribute types
-   *                             for this objectclass.
-   * @param  objectClassType     The objectclass type for this
-   *                             objectclass.
-   * @param  isObsolete          Indicates whether this objectclass is
-   *                             declared "obsolete".
-   * @param  extraProperties     A set of extra properties for this
-   *                             objectclass.
+   * @param primaryName
+   *          The primary name for this objectclass, or
+   *          <code>null</code> if there is no primary name.
+   * @param names
+   *          The set of names that may be used to reference this
+   *          objectclass, or <code>null</code> if there are no
+   *          name.
+   * @param oid
+   *          The OID for this objectclass (must not be
+   *          <code>null</code>).
+   * @param description
+   *          The description for this objectclass, or
+   *          <code>null</code> if there is no description.
+   * @param superiorClass
+   *          The superior class for this objectclass, or
+   *          <code>null</code> if there is no superior object
+   *          classes.
+   * @param requiredAttributes
+   *          The set of required attribute types for this
+   *          objectclass, or <code>null</code> if there are no
+   *          required attribute types.
+   * @param optionalAttributes
+   *          The set of optional attribute types for this
+   *          objectclass, or <code>null</code> if there are no
+   *          optional attribute types.
+   * @param objectClassType
+   *          The objectclass type for this objectclass, or
+   *          <code>null</code> to default to structural.
+   * @param isObsolete
+   *          Indicates whether this objectclass is declared
+   *          "obsolete".
+   * @param extraProperties
+   *          A set of extra properties for this objectclass, or
+   *          <code>null</code> if there are no extra properties.
+   * @throws NullPointerException
+   *           If the <code>oid</code> is <code>null</code>.
    */
-  public ObjectClass(String primaryName,
-             ConcurrentHashMap<String,String> names, String oid,
-             String description, ObjectClass superiorClass,
-             CopyOnWriteArraySet<AttributeType> requiredAttributes,
-             CopyOnWriteArraySet<AttributeType> optionalAttributes,
-             ObjectClassType objectClassType, boolean isObsolete,
-             ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
-                  extraProperties)
-  {
-    assert debugConstructor(CLASS_NAME,
-                            new String[]
-                            {
-                              String.valueOf(primaryName),
-                              String.valueOf(names),
-                              String.valueOf(oid),
-                              String.valueOf(description),
-                              String.valueOf(superiorClass),
-                              String.valueOf(requiredAttributes),
-                              String.valueOf(optionalAttributes),
-                              String.valueOf(objectClassType),
-                              String.valueOf(isObsolete),
-                              String.valueOf(extraProperties)
-                            });
+  public ObjectClass(String primaryName, Collection<String> names,
+      String oid, String description, ObjectClass superiorClass,
+      Set<AttributeType> requiredAttributes,
+      Set<AttributeType> optionalAttributes,
+      ObjectClassType objectClassType, boolean isObsolete,
+      Map<String, List<String>> extraProperties)
+      throws NullPointerException {
 
-    this.primaryName        = primaryName;
-    this.names              = names;
-    this.oid                = oid;
-    this.description        = description;
-    this.superiorClass      = superiorClass;
-    this.requiredAttributes = requiredAttributes;
-    this.optionalAttributes = optionalAttributes;
-    this.objectClassType    = objectClassType;
-    this.isObsolete         = isObsolete;
-    this.schemaFile         = null;
-    this.extraProperties    = extraProperties;
-  }
+    super(primaryName, names, oid, description, isObsolete,
+        extraProperties);
 
+    assert debugConstructor(CLASS_NAME, String.valueOf(primaryName),
+        String.valueOf(names), String.valueOf(oid), String
+            .valueOf(description), String.valueOf(superiorClass),
+        String.valueOf(requiredAttributes), String
+            .valueOf(optionalAttributes), String
+            .valueOf(objectClassType), String.valueOf(isObsolete),
+        String.valueOf(extraProperties));
 
+    this.superiorClass = superiorClass;
 
-  /**
-   * Retrieves the primary name for this objectclass.
-   *
-   * @return  The primary name for this objectClass, or
-   *          <CODE>null</CODE> if there is none.
-   */
-  public String getPrimaryName()
-  {
-    assert debugEnter(CLASS_NAME, "getPrimaryName");
-
-    return primaryName;
-  }
-
-
-
-  /**
-   * Specifies the primary name for this objectclass.
-   *
-   * @param  primaryName  The primary name for this objectclass.
-   */
-  public void setPrimaryName(String primaryName)
-  {
-    assert debugEnter(CLASS_NAME, "setPrimaryName",
-                      String.valueOf(primaryName));
-
-    this.primaryName = primaryName;
-
-    String lowerName = toLowerCase(primaryName);
-    names.put(lowerName, primaryName);
-  }
-
-
-
-  /**
-   * Retrieves the set of names that may be used to reference this
-   * objectclass.   The returned object will be a mapping between each
-   * name in all lowercase characters and that name in a user-defined
-   * form (which may include mixed capitalization).
-   *
-   * @return  The set of names that may be used to reference this
-   *          objectclass.
-   */
-  public ConcurrentHashMap<String,String> getNames()
-  {
-    assert debugEnter(CLASS_NAME, "getNames");
-
-    return names;
-  }
-
-
-
-  /**
-   * Specifies the set of names that may be used to reference this
-   * objectclass.  The provided set must provide a mapping between
-   * each name in all lowercase characters and that name in a
-   * user-defined form (which may include mixed capitalization).
-   *
-   * @param  names  The set of names that may be used to reference
-   *                this objectclass.
-   */
-  public void setNames(ConcurrentHashMap<String,String> names)
-  {
-    assert debugEnter(CLASS_NAME, "setNames", String.valueOf(names));
-
-    this.names = names;
-  }
-
-
-
-  /**
-   * Indicates whether the provided lowercase name may be used to
-   * reference this objectclass.
-   *
-   * @param  lowerName  The name for which to make the determination,
-   *                    in all lowercase characters.
-   *
-   * @return  <CODE>true</CODE> if the provided lowercase name may be
-   *          used to reference this objectclass, or
-   *          <CODE>false</CODE> if not.
-   */
-  public boolean hasName(String lowerName)
-  {
-    assert debugEnter(CLASS_NAME, "hasName",
-                      String.valueOf(lowerName));
-
-    return names.containsKey(lowerName);
-  }
-
-
-
-  /**
-   * Adds the provided name to the set of names that may be used to
-   * reference this objectclass.
-   *
-   * @param  name  The name to add to the set of names that may be
-   *               used to reference this objectclass.
-   */
-  public void addName(String name)
-  {
-    assert debugEnter(CLASS_NAME, "addName", String.valueOf(name));
-
-    String lowerName = toLowerCase(name);
-    names.put(lowerName, name);
-  }
-
-
-
-  /**
-   * Removes the provided lowercase name from the set of names that
-   * may be used to reference this objectclass.
-   *
-   * @param  lowerName  The name to remove from the set of names that
-   *                    may be used to reference this objectclass, in
-   *                    all lowercase characters.
-   */
-  public void removeName(String lowerName)
-  {
-    assert debugEnter(CLASS_NAME, "removeName",
-                      String.valueOf(lowerName));
-
-    names.remove(lowerName);
-    if (lowerName.equalsIgnoreCase(primaryName))
-    {
-      if (names.isEmpty())
-      {
-        primaryName = null;
-      }
-      else
-      {
-        primaryName = names.values().iterator().next();
-      }
-    }
-  }
-
-
-
-  /**
-   * Retrieves the OID for this objectclass.
-   *
-   * @return  The OID for this objectclass.
-   */
-  public String getOID()
-  {
-    assert debugEnter(CLASS_NAME, "getOID");
-
-    return oid;
-  }
-
-
-
-  /**
-   * Specifies the OID for this objectclass.
-   *
-   * @param  oid  The OID for this objectclass.
-   */
-  public void setOID(String oid)
-  {
-    assert debugEnter(CLASS_NAME, "setOID", String.valueOf(oid));
-
-    this.oid = oid;
-  }
-
-
-
-  /**
-   * Retrieves the name or OID that should be used to reference this
-   * objectclass.  If a primary name is defined, then that will be
-   * returned.  Otherwise, the OID will be returned.
-   *
-   * @return  The primary name if one is defined for this objectclass,
-   *          or the OID if there is no primary name.
-   */
-  public String getNameOrOID()
-  {
-    assert debugEnter(CLASS_NAME, "getNameOrOID");
-
-    if (primaryName == null)
-    {
-      return oid;
-    }
-    else
-    {
-      return primaryName;
-    }
-  }
-
-
-
-  /**
-   * Indicates whether the provided lowercase value is equal to the
-   * OID or any of the names that may be used to reference this
-   * objectclass.
-   *
-   * @param  lowerValue  The value, in all lowercase characters, that
-   *                     may be used to make the determination.
-   *
-   * @return  <CODE>true</CODE> if the provided lowercase value is one
-   *          of the names or the OID of this objectclass, or
-   *          <CODE>false</CODE> if it is not.
-   */
-  public boolean hasNameOrOID(String lowerValue)
-  {
-    assert debugEnter(CLASS_NAME, "hasNameOrOID",
-                      String.valueOf(lowerValue));
-
-    if (names.containsKey(lowerValue))
-    {
-      return true;
+    // Set flag indicating whether or not this object class allows any
+    // attributes.
+    if (hasName(OC_EXTENSIBLE_OBJECT_LC)
+        || oid.equals(OID_EXTENSIBLE_OBJECT)) {
+      this.isExtensibleObject = true;
+    } else {
+      this.isExtensibleObject = false;
     }
 
-    return lowerValue.equals(oid);
-  }
+    // Construct unmodifiable views of the required attributes.
+    if (requiredAttributes != null) {
+      this.requiredAttributes = Collections
+          .unmodifiableSet(new LinkedHashSet<AttributeType>(
+              requiredAttributes));
+    } else {
+      this.requiredAttributes = Collections.emptySet();
+    }
 
+    if (this.superiorClass == null) {
+      this.requiredAttributesChain = this.requiredAttributes;
+    } else {
+      Set<AttributeType> tmp = new HashSet<AttributeType>(
+          this.requiredAttributes);
+      tmp.addAll(this.superiorClass.getRequiredAttributeChain());
+      this.requiredAttributesChain = Collections.unmodifiableSet(tmp);
+    }
 
+    // Construct unmodifiable views of the optional attributes.
+    if (optionalAttributes != null) {
+      this.optionalAttributes = Collections
+          .unmodifiableSet(new LinkedHashSet<AttributeType>(
+              optionalAttributes));
+    } else {
+      this.optionalAttributes = Collections.emptySet();
+    }
 
-  /**
-   * Retrieves the path to the schema file that contains the
-   * definition for this objectclass.
-   *
-   * @return  The path to the schema file that contains the definition
-   *          for this objectclass, or <CODE>null</CODE> if it is not
-   *          known or if it is not stored in any schema file.
-   */
-  public String getSchemaFile()
-  {
-    assert debugEnter(CLASS_NAME, "getSchemaFile");
+    if (this.superiorClass == null) {
+      this.optionalAttributesChain = this.optionalAttributes;
+    } else {
+      Set<AttributeType> tmp = new HashSet<AttributeType>(
+          this.optionalAttributes);
+      tmp.addAll(this.superiorClass.getOptionalAttributeChain());
+      this.optionalAttributesChain = Collections.unmodifiableSet(tmp);
+    }
 
-    return schemaFile;
-  }
-
-
-
-  /**
-   * Specifies the path to the schema file that contains the
-   * definition for this objectclass.
-   *
-   * @param  schemaFile  The path to the schema file that contains the
-   *                     definition for this objectclass.
-   */
-  public void setSchemaFile(String schemaFile)
-  {
-    assert debugEnter(CLASS_NAME, "setSchemaFile",
-                      String.valueOf(schemaFile));
-
-    this.schemaFile = schemaFile;
-  }
-
-
-
-  /**
-   * Retrieves the description for this objectclass.
-   *
-   * @return  The description for this objectclass, or
-   *          <CODE>null</CODE> if there is none.
-   */
-  public String getDescription()
-  {
-    assert debugEnter(CLASS_NAME, "getDescription");
-
-    return description;
-  }
-
-
-
-  /**
-   * Specifies the description for this objectclass.
-   *
-   * @param  description  The description for this objectclass.
-   */
-  public void setDescription(String description)
-  {
-    assert debugEnter(CLASS_NAME, "setDescription",
-                      String.valueOf(description));
-
-    this.description = description;
+    // Object class type defaults to structural.
+    if (objectClassType != null) {
+      this.objectClassType = objectClassType;
+    } else {
+      this.objectClassType = ObjectClassType.STRUCTURAL;
+    }
   }
 
 
@@ -440,11 +217,10 @@
    * Retrieves the reference to the superior class for this
    * objectclass.
    *
-   * @return  The reference to the superior class for this
-   *          objectlass, or <CODE>null</CODE> if there is none.
+   * @return The reference to the superior class for this objectlass,
+   *         or <code>null</code> if there is none.
    */
-  public ObjectClass getSuperiorClass()
-  {
+  public ObjectClass getSuperiorClass() {
     assert debugEnter(CLASS_NAME, "getSuperiorClass");
 
     return superiorClass;
@@ -453,55 +229,37 @@
 
 
   /**
-   * Specifies the superior class for this objectclass.
-   *
-   * @param  superiorClass  The superior class for this objectclass.
-   */
-  public void setSuperiorClass(ObjectClass superiorClass)
-  {
-    assert debugEnter(CLASS_NAME, "setSuperiorClass",
-                      String.valueOf(superiorClass));
-
-    this.superiorClass = superiorClass;
-  }
-
-
-
-  /**
    * Indicates whether this objectclass is a descendant of the
    * provided class.
    *
-   * @param  objectClass  The objectClass for which to make the
-   *                      determination.
-   *
-   * @return  <CODE>true</CODE> if this objectclass is a descendant of
-   *          the provided class, or <CODE>false</CODE> if not.
+   * @param objectClass
+   *          The objectClass for which to make the determination.
+   * @return <code>true</code> if this objectclass is a descendant
+   *         of the provided class, or <code>false</code> if not.
    */
-  public boolean isDescendantOf(ObjectClass objectClass)
-  {
-    assert debugEnter(CLASS_NAME, "isDescendantOf",
-                      String.valueOf(objectClass));
+  public boolean isDescendantOf(ObjectClass objectClass) {
+    assert debugEnter(CLASS_NAME, "isDescendantOf", String
+        .valueOf(objectClass));
 
-    if (superiorClass == null)
-    {
+    if (superiorClass == null) {
       return false;
     }
 
-    return (superiorClass.equals(objectClass) ||
-            superiorClass.isDescendantOf(objectClass));
+    return (superiorClass.equals(objectClass) || superiorClass
+        .isDescendantOf(objectClass));
   }
 
 
 
   /**
-   * Retrieves the set of required attributes for this objectclass.
-   * Note that this list will not automatically include any required
-   * attributes for superior objectclasses.
+   * Retrieves an unmodifiable view of the set of required attributes
+   * for this objectclass. Note that this set will not automatically
+   * include any required attributes for superior objectclasses.
    *
-   * @return  The set of required attributes for this objectclass.
+   * @return Returns an unmodifiable view of the set of required
+   *         attributes for this objectclass.
    */
-  public CopyOnWriteArraySet<AttributeType> getRequiredAttributes()
-  {
+  public Set<AttributeType> getRequiredAttributes() {
     assert debugEnter(CLASS_NAME, "getRequiredAttributes");
 
     return requiredAttributes;
@@ -510,27 +268,18 @@
 
 
   /**
-   * Retrieves the set of all required attributes for this objectclass
-   * and any superior objectclasses that it might have.
+   * Retrieves an unmodifiable view of the set of all required
+   * attributes for this objectclass and any superior objectclasses
+   * that it might have.
    *
-   * @return  The set of all required attributes for this objectclass
-   *          and any superior objectclasses that it might have.
+   * @return Returns an unmodifiable view of the set of all required
+   *         attributes for this objectclass and any superior
+   *         objectclasses that it might have.
    */
-  public Set<AttributeType> getRequiredAttributeChain()
-  {
+  public Set<AttributeType> getRequiredAttributeChain() {
     assert debugEnter(CLASS_NAME, "getRequiredAttributeChain");
 
-    if (superiorClass == null)
-    {
-      return requiredAttributes;
-    }
-    else
-    {
-      HashSet<AttributeType> attrs = new HashSet<AttributeType>();
-      attrs.addAll(requiredAttributes);
-      attrs.addAll(superiorClass.getRequiredAttributeChain());
-      return attrs;
-    }
+    return requiredAttributesChain;
   }
 
 
@@ -540,94 +289,30 @@
    * required attribute list for this or any of its superior
    * objectclasses.
    *
-   * @param  attributeType  The attribute type for which to make the
-   *                        determination.
-   *
-   * @return  <CODE>true</CODE> if the provided attribute type is
-   *          required by this objectclass or any of its superior
-   *          classes, or <CODE>false</CODE> if not.
+   * @param attributeType
+   *          The attribute type for which to make the determination.
+   * @return <code>true</code> if the provided attribute type is
+   *         required by this objectclass or any of its superior
+   *         classes, or <code>false</code> if not.
    */
-  public boolean isRequired(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "isRequired",
-                      String.valueOf(attributeType));
+  public boolean isRequired(AttributeType attributeType) {
+    assert debugEnter(CLASS_NAME, "isRequired", String
+        .valueOf(attributeType));
 
-    if (requiredAttributes.contains(attributeType))
-    {
-      return true;
-    }
-
-    if (superiorClass != null)
-    {
-      return superiorClass.isRequired(attributeType);
-    }
-
-    return false;
+    return requiredAttributesChain.contains(attributeType);
   }
 
 
 
   /**
-   * Specifies the set of required attributes for this objectclass.
+   * Retrieves an unmodifiable view of the set of optional attributes
+   * for this objectclass. Note that this list will not automatically
+   * include any optional attributes for superior objectclasses.
    *
-   * @param  requiredAttributes  The set of required attributes for
-   *                             this objectclass.
+   * @return Returns an unmodifiable view of the set of optional
+   *         attributes for this objectclass.
    */
-  public void setRequiredAttributes(CopyOnWriteArraySet<AttributeType>
-                                         requiredAttributes)
-  {
-    assert debugEnter(CLASS_NAME, "setRequiredAttributes",
-                      String.valueOf(requiredAttributes));
-
-    this.requiredAttributes = requiredAttributes;
-  }
-
-
-
-  /**
-   * Adds the provided attribute to the set of required attributes for
-   * this objectclass.
-   *
-   * @param  attributeType  The attribute type to add to the set of
-   *                        required attributes for this objectclass.
-   */
-  public void addRequiredAttribute(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "addRequiredAttribute",
-                      String.valueOf(attributeType));
-
-    requiredAttributes.add(attributeType);
-  }
-
-
-
-  /**
-   * Removes the provided attribute from the set of required
-   * attributes for this objectclass.
-   *
-   * @param  attributeType  The attribute type to remove from the set
-   *                        of required attributes for this
-   *                        objectclass.
-   */
-  public void removeRequiredAttribute(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "removeRequiredAttribute",
-                      String.valueOf(attributeType));
-
-    requiredAttributes.remove(attributeType);
-  }
-
-
-
-  /**
-   * Retrieves the set of optional attributes for this objectclass.
-   * Note that this list will not automatically include any optional
-   * attributes for superior objectclasses.
-   *
-   * @return  The set of optional attributes for this objectclass.
-   */
-  public CopyOnWriteArraySet<AttributeType> getOptionalAttributes()
-  {
+  public Set<AttributeType> getOptionalAttributes() {
     assert debugEnter(CLASS_NAME, "getOptionalAttributes");
 
     return optionalAttributes;
@@ -636,27 +321,18 @@
 
 
   /**
-   * Retrieves the set of all optional attributes for this objectclass
-   * and any superior objectclasses that it might have.
+   * Retrieves an unmodifiable view of the set of optional attributes
+   * for this objectclass and any superior objectclasses that it might
+   * have.
    *
-   * @return  The set of all optional attributes for this objectclass
-   *          and any superior objectclasses that it might have.
+   * @return Returns an unmodifiable view of the set of optional
+   *         attributes for this objectclass and any superior
+   *         objectclasses that it might have.
    */
-  public Set<AttributeType> getOptionalAttributeChain()
-  {
+  public Set<AttributeType> getOptionalAttributeChain() {
     assert debugEnter(CLASS_NAME, "getOptionalAttributeChain");
 
-    if (superiorClass == null)
-    {
-      return optionalAttributes;
-    }
-    else
-    {
-      HashSet<AttributeType> attrs = new HashSet<AttributeType>();
-      attrs.addAll(optionalAttributes);
-      attrs.addAll(superiorClass.getOptionalAttributeChain());
-      return attrs;
-    }
+    return optionalAttributesChain;
   }
 
 
@@ -666,131 +342,49 @@
    * optional attribute list for this or any of its superior
    * objectclasses.
    *
-   * @param  attributeType  The attribute type for which to make the
-   *                        determination.
-   *
-   * @return  <CODE>true</CODE> if the provided attribute type is
-   *          optional for this objectclass or any of its superior
-   *          classes, or <CODE>false</CODE> if not.
+   * @param attributeType
+   *          The attribute type for which to make the determination.
+   * @return <code>true</code> if the provided attribute type is
+   *         optional for this objectclass or any of its superior
+   *         classes, or <code>false</code> if not.
    */
-  public boolean isOptional(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "isOptional",
-                      String.valueOf(attributeType));
+  public boolean isOptional(AttributeType attributeType) {
+    assert debugEnter(CLASS_NAME, "isOptional", String
+        .valueOf(attributeType));
 
-    if (optionalAttributes.contains(attributeType))
-    {
+    if (optionalAttributesChain.contains(attributeType)) {
       return true;
     }
 
-    if (isExtensibleObject() &&
-        (! requiredAttributes.contains(attributeType)))
-    {
+    if (isExtensibleObject
+        && !requiredAttributesChain.contains(attributeType)) {
       // FIXME -- Do we need to do other checks here, like whether the
-      //          attribute type is actually defined in the schema?
-      //          What about DIT content rules?
+      // attribute type is actually defined in the schema?
+      // What about DIT content rules?
       return true;
     }
 
-    if (superiorClass != null)
-    {
-      return superiorClass.isOptional(attributeType);
-    }
-
     return false;
   }
 
 
 
   /**
-   * Specifies the set of optional attributes for this objectclass.
-   *
-   * @param  optionalAttributes  The set of optional attributes for
-   *                             this objectclass.
-   */
-  public void setOptionalAttributes(CopyOnWriteArraySet<AttributeType>
-                                         optionalAttributes)
-  {
-    assert debugEnter(CLASS_NAME, "setOptionalAttributes",
-                      String.valueOf(optionalAttributes));
-
-    this.optionalAttributes = optionalAttributes;
-  }
-
-
-
-  /**
-   * Adds the provided attribute to the set of optional attributes for
-   * this objectclass.
-   *
-   * @param  attributeType  The attribute type to add to the set of
-   *                        optional attributes for this objectclass.
-   */
-  public void addOptionalAttribute(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "addOptionalAttribute",
-                      String.valueOf(attributeType));
-
-    optionalAttributes.add(attributeType);
-  }
-
-
-
-  /**
-   * Removes the provided attribute from the set of optional
-   * attributes for this objectclass.
-   *
-   * @param  attributeType  The attribute type to remove from the set
-   *                        of optional attributes for this
-   *                        objectclass.
-   */
-  public void removeOptionalAttribute(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "removeOptionalAttribute",
-                      String.valueOf(attributeType));
-
-    optionalAttributes.remove(attributeType);
-  }
-
-
-
-  /**
    * Indicates whether the provided attribute type is in the list of
    * required or optional attributes for this objectclass or any of
    * its superior classes.
    *
-   * @param  attributeType  The attribute type for which to make the
-   *                        determination.
-   *
-   * @return  <CODE>true</CODE> if the provided attribute type is
-   *          required or allowed for this objectclass or any of its
-   *          superior classes, or <CODE>false</CODE> if it is not.
+   * @param attributeType
+   *          The attribute type for which to make the determination.
+   * @return <code>true</code> if the provided attribute type is
+   *         required or allowed for this objectclass or any of its
+   *         superior classes, or <code>false</code> if it is not.
    */
-  public boolean isRequiredOrOptional(AttributeType attributeType)
-  {
-    assert debugEnter(CLASS_NAME, "isRequiredOrOptional",
-                      String.valueOf(attributeType));
+  public boolean isRequiredOrOptional(AttributeType attributeType) {
+    assert debugEnter(CLASS_NAME, "isRequiredOrOptional", String
+        .valueOf(attributeType));
 
-    if (requiredAttributes.contains(attributeType) ||
-        optionalAttributes.contains(attributeType))
-    {
-      return true;
-    }
-
-    if (isExtensibleObject())
-    {
-      // FIXME -- Do we need to do other checks here, like whether the
-      //          attribute type is actually defined in the schema?
-      //          What about DIT content rules?
-      return true;
-    }
-
-    if (superiorClass != null)
-    {
-      return superiorClass.isRequiredOrOptional(attributeType);
-    }
-
-    return false;
+    return (isRequired(attributeType) || isOptional(attributeType));
   }
 
 
@@ -798,10 +392,9 @@
   /**
    * Retrieves the objectclass type for this objectclass.
    *
-   * @return  The objectclass type for this objectclass.
+   * @return The objectclass type for this objectclass.
    */
-  public ObjectClassType getObjectClassType()
-  {
+  public ObjectClassType getObjectClassType() {
     assert debugEnter(CLASS_NAME, "getObjectClassType");
 
     return objectClassType;
@@ -810,345 +403,78 @@
 
 
   /**
-   * Specifies the objectclass type for this objectclass.
-   *
-   * @param  objectClassType  The objectclass type for this
-   *                          objectclass.
-   */
-  public void setObjectClassType(ObjectClassType objectClassType)
-  {
-    assert debugEnter(CLASS_NAME, "setObjectClassType",
-                      String.valueOf(objectClassType));
-  }
-
-
-
-  /**
-   * Indicates whether this objectclass is declared "obsolete".
-   *
-   * @return  <CODE>true</CODE> if this objectclass is declared
-   *          "obsolete", or <CODE>false</CODE> if it is not.
-   */
-  public boolean isObsolete()
-  {
-    assert debugEnter(CLASS_NAME, "isObsolete");
-
-    return isObsolete;
-  }
-
-
-
-  /**
-   * Specifies whether this objectclass is declared "obsolete".
-   *
-   * @param  isObsolete  Specifies whether this objectclass is
-   *                     declared "obsolete".
-   */
-  public void setObsolete(boolean isObsolete)
-  {
-    assert debugEnter(CLASS_NAME, "setObsolete",
-                      String.valueOf(isObsolete));
-
-    this.isObsolete = isObsolete;
-  }
-
-
-
-  /**
    * Indicates whether this objectclass is the extensibleObject
    * objectclass.
    *
-   * @return  <CODE>true</CODE> if this objectclass is the
-   *          extensibleObject objectclass, or <CODE>false</CODE> if
-   *          it is not.
+   * @return <code>true</code> if this objectclass is the
+   *         extensibleObject objectclass, or <code>false</code> if
+   *         it is not.
    */
-  public boolean isExtensibleObject()
-  {
+  public boolean isExtensibleObject() {
     assert debugEnter(CLASS_NAME, "isExtensibleObject");
 
-    return (names.containsKey(OC_EXTENSIBLE_OBJECT_LC) ||
-            oid.equals(OID_EXTENSIBLE_OBJECT));
+    return isExtensibleObject;
   }
 
 
 
   /**
-   * Retrieves a mapping between the names of any extra non-standard
-   * properties that may be associated with this objectclass and the
-   * value for that property.  The caller may alter the contents of
-   * this mapping.
-   *
-   * @return  A mapping between the names of any extra non-standard
-   *          properties that may be associated with this objectclass
-   *          and the value for that property.
+   * {@inheritDoc}
    */
-  public ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
-              getExtraProperties()
-  {
-    assert debugEnter(CLASS_NAME, "getExtraProperties");
-
-    return extraProperties;
-  }
-
-
-
-  /**
-   * Retrieves the value of the specified "extra" property for this
-   * objectclass.
-   *
-   * @param  propertyName  The name of the "extra" property for which
-   *                       to retrieve the value.
-   *
-   * @return  The value of the specified "extra" property for this
-   *          objectclass, or <CODE>null</CODE> if no such property is
-   *          defined.
-   */
-  public CopyOnWriteArrayList<String>
-              getExtraProperty(String propertyName)
-  {
-    assert debugEnter(CLASS_NAME, "getExtraProperty",
-                      String.valueOf(propertyName));
-
-    return extraProperties.get(propertyName);
-  }
-
-
-
-  /**
-   * Indicates whether the provided object is equal to this
-   * objectclass.  The object will be considered equal if it is an
-   * attribute type with the same OID as the current type.
-   *
-   * @param  o  The object for which to make the determination.
-   *
-   * @return  <CODE>true</CODE> if the provided object is equal to
-   *          this objectclass, or <CODE>false</CODE> if not.
-   */
-  public boolean equals(Object o)
-  {
-    assert debugEnter(CLASS_NAME, "equals");
-
-    if (this == o)
-    {
-      return true;
-    }
-
-    if ((o == null) || (! (o instanceof ObjectClass)))
-    {
-      return false;
-    }
-
-    return oid.equals(((ObjectClass) o).oid);
-  }
-
-
-
-  /**
-   * Retrieves the hash code for this objectclass.  It will be based
-   * on the sum of the bytes of the OID.
-   *
-   * @return  The hash code for this objectclass.
-   */
-  public int hashCode()
-  {
-    assert debugEnter(CLASS_NAME, "hashCode");
-
-    int oidLength = oid.length();
-    int hashCode  = 0;
-    for (int i=0; i < oidLength; i++)
-    {
-      hashCode += oid.charAt(i);
-    }
-
-    return hashCode;
-  }
-
-
-
-  /**
-   * Retrieves the string representation of this objectclass in the
-   * form specified in RFC 2252.
-   *
-   * @return  The string representation of this objectclass in the
-   *          form specified in RFC 2252.
-   */
-  public String toString()
-  {
-    assert debugEnter(CLASS_NAME, "toString");
-
-    StringBuilder buffer = new StringBuilder();
-    toString(buffer, true);
-    return buffer.toString();
-  }
-
-
-
-  /**
-   * Appends a string representation of this objectclass in the form
-   * specified in RFC 2252 to the provided buffer.
-   *
-   * @param  buffer              The buffer to which the information
-   *                             should be appended.
-   * @param  includeFileElement  Indicates whether to include an
-   *                             "extra" property that specifies the
-   *                             path to the schema file from which
-   *                             this objectclass was loaded.
-   */
-  public void toString(StringBuilder buffer,
-                       boolean includeFileElement)
-  {
+  protected void toStringContent(StringBuilder buffer) {
     assert debugEnter(CLASS_NAME, "toString",
-                      "java.lang.StringBuilder",
-                      String.valueOf(includeFileElement));
+        "java.lang.StringBuilder");
 
-    buffer.append("( ");
-    buffer.append(oid);
-
-    if (! names.isEmpty())
-    {
-      Iterator<String> iterator = names.values().iterator();
-
-      String firstName = iterator.next();
-      if (iterator.hasNext())
-      {
-        buffer.append(" NAME ( '");
-        buffer.append(firstName);
-
-        while (iterator.hasNext())
-        {
-          buffer.append("' '");
-          buffer.append(iterator.next());
-        }
-
-        buffer.append("' )");
-      }
-      else
-      {
-        buffer.append(" NAME '");
-        buffer.append(firstName);
-        buffer.append("'");
-      }
-    }
-
-    if ((description != null) && (description.length() > 0))
-    {
-      buffer.append(" DESC '");
-      buffer.append(description);
-      buffer.append("'");
-    }
-
-    if (isObsolete)
-    {
-      buffer.append(" OBSOLETE");
-    }
-
-    if (superiorClass != null)
-    {
+    if (superiorClass != null) {
       buffer.append(" SUP ");
       buffer.append(superiorClass.getNameOrOID());
     }
 
-    if (objectClassType != null)
-    {
+    if (objectClassType != null) {
       buffer.append(" ");
       buffer.append(objectClassType.toString());
     }
 
-    if (! requiredAttributes.isEmpty())
-    {
-      Iterator<AttributeType> iterator =
-           requiredAttributes.iterator();
+    if (!requiredAttributes.isEmpty()) {
+      Iterator<AttributeType> iterator = requiredAttributes
+          .iterator();
 
       String firstName = iterator.next().getNameOrOID();
-      if (iterator.hasNext())
-      {
+      if (iterator.hasNext()) {
         buffer.append(" MUST ( ");
         buffer.append(firstName);
 
-        while (iterator.hasNext())
-        {
+        while (iterator.hasNext()) {
           buffer.append(" $ ");
           buffer.append(iterator.next().getNameOrOID());
         }
 
         buffer.append(" )");
-      }
-      else
-      {
+      } else {
         buffer.append(" MUST ");
         buffer.append(firstName);
       }
     }
 
-    if (! optionalAttributes.isEmpty())
-    {
-      Iterator<AttributeType> iterator =
-           optionalAttributes.iterator();
+    if (!optionalAttributes.isEmpty()) {
+      Iterator<AttributeType> iterator = optionalAttributes
+          .iterator();
 
       String firstName = iterator.next().getNameOrOID();
-      if (iterator.hasNext())
-      {
+      if (iterator.hasNext()) {
         buffer.append(" MAY ( ");
         buffer.append(firstName);
 
-        while (iterator.hasNext())
-        {
+        while (iterator.hasNext()) {
           buffer.append(" $ ");
           buffer.append(iterator.next().getNameOrOID());
         }
 
         buffer.append(" )");
-      }
-      else
-      {
+      } else {
         buffer.append(" MAY ");
         buffer.append(firstName);
       }
     }
-
-    if (! extraProperties.isEmpty())
-    {
-      for (String property : extraProperties.keySet())
-      {
-        CopyOnWriteArrayList<String> valueList =
-             extraProperties.get(property);
-
-        buffer.append(" ");
-        buffer.append(property);
-
-        if (valueList.size() == 1)
-        {
-          buffer.append(" '");
-          buffer.append(valueList.get(0));
-          buffer.append("'");
-        }
-        else
-        {
-          buffer.append(" ( ");
-
-          for (String value : valueList)
-          {
-            buffer.append("'");
-            buffer.append(value);
-            buffer.append("' ");
-          }
-
-          buffer.append(")");
-        }
-      }
-    }
-
-    if (includeFileElement && (schemaFile != null) &&
-        (! extraProperties.containsKey(SCHEMA_PROPERTY_FILENAME)))
-    {
-      buffer.append(" ");
-      buffer.append(SCHEMA_PROPERTY_FILENAME);
-      buffer.append(" '");
-      buffer.append(schemaFile);
-      buffer.append("'");
-    }
-
-    buffer.append(" )");
   }
 }
-
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java
index 6e5def2..949bfde 100644
--- a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java
@@ -26,14 +26,12 @@
  */
 package org.opends.server.types;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.LinkedList;
+
+
+import java.util.Collection;
 import java.util.List;
 import java.util.Map;
 
-import org.opends.server.TestCaseUtils;
 import org.opends.server.api.ApproximateMatchingRule;
 import org.opends.server.api.AttributeSyntax;
 import org.opends.server.api.EqualityMatchingRule;
@@ -43,31 +41,21 @@
 import org.opends.server.protocols.asn1.ASN1OctetString;
 import org.opends.server.util.ServerConstants;
 import org.testng.Assert;
-import org.testng.annotations.BeforeClass;
 import org.testng.annotations.DataProvider;
 import org.testng.annotations.Test;
 
+
+
 /**
  * This class defines a set of tests for the
  * {@link org.opends.server.types.AttributeType} class.
  */
-public final class TestAttributeType extends TypesTestCase {
+public final class TestAttributeType extends TestCommonSchemaElements {
   /**
    * Internal class to simplify construction of attribute types.
    */
-  private static final class AttributeTypeBuilder {
-    // The primary name to use for this attribute type.
-    private String primaryName;
-
-    // The set of names for this attribute type.
-    private List<String> typeNames;
-
-    // The OID that may be used to reference this attribute type.
-    private String oid;
-
-    // The description for this attribute type.
-    private String description;
-
+  private static final class AttributeTypeBuilder extends
+      SchemaDefinitionBuilder<AttributeType> {
     // The superior attribute type from which this attribute type
     // inherits.
     private AttributeType superiorType;
@@ -97,23 +85,16 @@
     // "no-user-modification".
     private boolean isNoUserModification;
 
-    // Indicates whether this attribute type is declared "obsolete".
-    private boolean isObsolete;
-
     // Indicates whether this attribute type is declared
     // "single-value".
     private boolean isSingleValue;
 
-    // The set of additional name-value pairs associated with this
-    // attribute type definition.
-    private Map<String, List<String>> extraProperties;
 
-    // Reset the builder to its initial state.
-    private void reset() {
-      this.primaryName = null;
-      this.typeNames = null;
-      this.oid = null;
-      this.description = null;
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void resetBuilder() {
       this.superiorType = null;
       this.syntax = null;
       this.approximateMatchingRule = null;
@@ -123,60 +104,54 @@
       this.attributeUsage = AttributeUsage.USER_APPLICATIONS;
       this.isCollective = false;
       this.isNoUserModification = false;
-      this.isObsolete = false;
       this.isSingleValue = false;
-      this.extraProperties = null;
     }
 
+
+
     /**
      * Create a new attribute type builder.
      */
     public AttributeTypeBuilder() {
-      reset();
+      super();
     }
 
+
+
     /**
      * Create a new attribute type builder.
-     * 
+     *
      * @param primaryName
      *          The attribute type primary name.
      * @param oid
      *          The attribute type OID.
      */
     public AttributeTypeBuilder(String primaryName, String oid) {
-      reset();
-
-      this.primaryName = primaryName;
-      this.oid = oid;
+      super(primaryName, oid);
     }
 
+
+
     /**
-     * Construct an attribute type based on the properties of the
-     * builder.
-     * 
-     * @return The new attribute type.
+     * {@inheritDoc}
      */
-    public AttributeType getAttributeType() {
-      if (oid == null) {
-        throw new IllegalStateException("Null OID.");
-      }
-
-      AttributeType type = new AttributeType(primaryName, typeNames,
-          oid, description, superiorType, syntax,
-          approximateMatchingRule, equalityMatchingRule,
-          orderingMatchingRule, substringMatchingRule,
-          attributeUsage, isCollective, isNoUserModification,
-          isObsolete, isSingleValue, extraProperties);
-
-      // Reset the internal state.
-      reset();
-
-      return type;
+    @Override
+    protected AttributeType buildInstance(String primaryName,
+        Collection<String> names, String oid, String description,
+        boolean isObsolete, Map<String, List<String>> extraProperties) {
+      return new AttributeType(primaryName, names, oid, description,
+          superiorType, syntax, approximateMatchingRule,
+          equalityMatchingRule, orderingMatchingRule,
+          substringMatchingRule, attributeUsage, isCollective,
+          isNoUserModification, isObsolete, isSingleValue,
+          extraProperties);
     }
 
+
+
     /**
      * Set the approximateMatchingRule.
-     * 
+     *
      * @param approximateMatchingRule
      *          The approximateMatchingRule.
      */
@@ -185,9 +160,11 @@
       this.approximateMatchingRule = approximateMatchingRule;
     }
 
+
+
     /**
      * Set the attributeUsage.
-     * 
+     *
      * @param attributeUsage
      *          The attributeUsage.
      */
@@ -195,19 +172,11 @@
       this.attributeUsage = attributeUsage;
     }
 
-    /**
-     * Set the description.
-     * 
-     * @param description
-     *          The description.
-     */
-    public void setDescription(String description) {
-      this.description = description;
-    }
+
 
     /**
      * Set the equalityMatchingRule.
-     * 
+     *
      * @param equalityMatchingRule
      *          The equalityMatchingRule.
      */
@@ -216,38 +185,11 @@
       this.equalityMatchingRule = equalityMatchingRule;
     }
 
-    /**
-     * Add extra property value(s).
-     * 
-     * @param name
-     *          The name of the extra property.
-     * @param values
-     *          The value(s) of the extra property.
-     */
-    public void addExtraProperty(String name, String... values) {
-      if (name == null) {
-        throw new NullPointerException("Null extra property name");
-      }
 
-      if (values == null) {
-        throw new NullPointerException("Null extra property values");
-      }
-
-      if (extraProperties == null) {
-        extraProperties = new HashMap<String, List<String>>();
-      }
-
-      List<String> l = extraProperties.get(name);
-      if (l == null) {
-        l = new ArrayList<String>();
-        extraProperties.put(name, l);
-      }
-      l.addAll(Arrays.asList(values));
-    }
 
     /**
      * Set the isCollective.
-     * 
+     *
      * @param isCollective
      *          The isCollective.
      */
@@ -255,9 +197,11 @@
       this.isCollective = isCollective;
     }
 
+
+
     /**
      * Set the isNoUserModification.
-     * 
+     *
      * @param isNoUserModification
      *          The isNoUserModification.
      */
@@ -265,19 +209,11 @@
       this.isNoUserModification = isNoUserModification;
     }
 
-    /**
-     * Set the isObsolete.
-     * 
-     * @param isObsolete
-     *          The isObsolete.
-     */
-    public void setObsolete(boolean isObsolete) {
-      this.isObsolete = isObsolete;
-    }
+
 
     /**
      * Set the isSingleValue.
-     * 
+     *
      * @param isSingleValue
      *          The isSingleValue.
      */
@@ -285,23 +221,11 @@
       this.isSingleValue = isSingleValue;
     }
 
-    /**
-     * Set the oid.
-     * 
-     * @param oid
-     *          The oid.
-     */
-    public void setOid(String oid) {
-      if (oid == null) {
-        throw new NullPointerException("Null OID");
-      }
 
-      this.oid = oid;
-    }
 
     /**
      * Set the orderingMatchingRule.
-     * 
+     *
      * @param orderingMatchingRule
      *          The orderingMatchingRule.
      */
@@ -310,19 +234,11 @@
       this.orderingMatchingRule = orderingMatchingRule;
     }
 
-    /**
-     * Set the primaryName.
-     * 
-     * @param primaryName
-     *          The primaryName.
-     */
-    public void setPrimaryName(String primaryName) {
-      this.primaryName = primaryName;
-    }
+
 
     /**
      * Set the substringMatchingRule.
-     * 
+     *
      * @param substringMatchingRule
      *          The substringMatchingRule.
      */
@@ -331,9 +247,11 @@
       this.substringMatchingRule = substringMatchingRule;
     }
 
+
+
     /**
      * Set the superiorType.
-     * 
+     *
      * @param superiorType
      *          The superiorType.
      */
@@ -341,53 +259,25 @@
       this.superiorType = superiorType;
     }
 
+
+
     /**
      * Set the syntax.
-     * 
+     *
      * @param syntax
      *          The syntax.
      */
     public void setSyntax(AttributeSyntax syntax) {
       this.syntax = syntax;
     }
-
-    /**
-     * Add attribute type name(s).
-     * 
-     * @param names
-     *          The attribute type name(s) to add.
-     */
-    public void addTypeNames(String... names) {
-      if (names == null) {
-        throw new NullPointerException("Null names");
-      }
-
-      if (typeNames == null) {
-        typeNames = new LinkedList<String>();
-      }
-
-      typeNames.addAll(Arrays.asList(names));
-    }
   }
 
-  /**
-   * Once-only initialization.
-   * 
-   * @throws Exception
-   *           If an unexpected error occurred.
-   */
-  @BeforeClass
-  public void setUp() throws Exception {
-    // This test suite depends on having the schema available, so
-    // we'll
-    // start the server.
-    TestCaseUtils.startServer();
-  }
+
 
   /**
    * Check that the simple constructor throws an NPE when mandatory
    * parameters are not specified.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -397,10 +287,12 @@
         false, false, false, false);
   }
 
+
+
   /**
    * Check that the complex constructor throws an NPE when mandatory
    * parameters are not specified.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -410,10 +302,12 @@
         null, null, null, false, false, false, false, null);
   }
 
+
+
   /**
    * Check that the complex constructor does not throw an exception
    * when all optional parameters are not specified.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -426,44 +320,11 @@
     Assert.assertNull(type.getPrimaryName());
   }
 
-  /**
-   * Check that the primary name is added to the set of names.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testConstructorPrimaryName() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
 
-    Assert.assertTrue(type.hasName("testtype"));
-    Assert.assertFalse(type.hasName("xxx"));
-  }
-
-  /**
-   * Check that the type names are accessible.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testConstructorTypeNames() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-
-    builder.addTypeNames("testNameAlias", "anotherNameAlias");
-    AttributeType type = builder.getAttributeType();
-
-    Assert.assertTrue(type.hasName("testtype"));
-    Assert.assertTrue(type.hasName("testnamealias"));
-    Assert.assertTrue(type.hasName("anothernamealias"));
-  }
 
   /**
    * Check constructor sets the default usage correctly.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -471,15 +332,17 @@
   public void testConstructorDefaultUsage() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertEquals(type.getUsage(),
         AttributeUsage.USER_APPLICATIONS);
   }
 
+
+
   /**
    * Check constructor sets the default syntax correctly.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -487,15 +350,17 @@
   public void testConstructorDefaultSyntax() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertEquals(type.getSyntax(), DirectoryServer
         .getDefaultAttributeSyntax());
   }
 
+
+
   /**
    * Check constructor sets the syntax correctly.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -504,16 +369,18 @@
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
     builder.setSyntax(DirectoryServer.getDefaultIntegerSyntax());
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertEquals(type.getSyntax(), DirectoryServer
         .getDefaultIntegerSyntax());
   }
 
+
+
   /**
    * Check constructor inherits the syntax from the parent type when
    * required.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -522,19 +389,21 @@
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "parentType", "1.2.3");
     builder.setSyntax(DirectoryServer.getDefaultIntegerSyntax());
-    AttributeType parent = builder.getAttributeType();
+    AttributeType parent = builder.getInstance();
 
     builder.setPrimaryName("childType");
     builder.setOid("4.5.6");
     builder.setSuperiorType(parent);
-    AttributeType child = builder.getAttributeType();
+    AttributeType child = builder.getInstance();
 
     Assert.assertEquals(parent.getSyntax(), child.getSyntax());
   }
 
+
+
   /**
    * Check constructor sets the default matching rules correctly.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -542,7 +411,7 @@
   public void testConstructorDefaultMatchingRules() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     AttributeSyntax syntax = DirectoryServer
         .getDefaultAttributeSyntax();
@@ -556,9 +425,11 @@
         .getSubstringMatchingRule());
   }
 
+
+
   /**
    * Check constructor sets the matching rules correctly.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -573,7 +444,7 @@
     builder.setOrderingMatchingRule(syntax.getOrderingMatchingRule());
     builder.setSubstringMatchingRule(syntax
         .getSubstringMatchingRule());
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertEquals(type.getApproximateMatchingRule(), syntax
         .getApproximateMatchingRule());
@@ -585,10 +456,12 @@
         .getSubstringMatchingRule());
   }
 
+
+
   /**
    * Check constructor inherits the matching rules from the parent
    * type when required.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -604,12 +477,12 @@
     builder.setSubstringMatchingRule(syntax
         .getSubstringMatchingRule());
 
-    AttributeType parent = builder.getAttributeType();
+    AttributeType parent = builder.getInstance();
 
     builder.setPrimaryName("childType");
     builder.setOid("4.5.6");
     builder.setSuperiorType(parent);
-    AttributeType child = builder.getAttributeType();
+    AttributeType child = builder.getInstance();
 
     Assert.assertEquals(parent.getApproximateMatchingRule(), child
         .getApproximateMatchingRule());
@@ -621,10 +494,12 @@
         .getSubstringMatchingRule());
   }
 
+
+
   /**
    * Create test data for testing the
    * {@link AttributeType#isObjectClassType()} method.
-   * 
+   *
    * @return Returns the array of test data.
    */
   @DataProvider(name = "isObjectClassTypeTestData")
@@ -634,10 +509,12 @@
         { "OBJECTCLASS", true } };
   }
 
+
+
   /**
    * Check that the objectClass attribute type is correctly
    * identified.
-   * 
+   *
    * @param name
    *          The primary name.
    * @param result
@@ -650,91 +527,17 @@
       throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(name,
         "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertEquals(type.isObjectClassType(), result);
   }
 
-  /**
-   * Create test data for testing the
-   * {@link AttributeType#equals(Object)} method.
-   * 
-   * @return Returns the array of test data.
-   */
-  @DataProvider(name = "equalsTestData")
-  public Object[][] createEqualsTestData() {
-    return new Object[][] {
-        { "testType", "1.2.3", "testType", "1.2.3", true },
-        { "testType", "1.2.3", "xxx", "1.2.3", true },
-        { "testType", "1.2.3", "testType", "1.2.4", false },
-        { "testType", "1.2.3", "xxx", "1.2.4", false } };
-  }
 
-  /**
-   * Check that the equals operator works as expected.
-   * 
-   * @param name1
-   *          The first primary name.
-   * @param oid1
-   *          The first oid.
-   * @param name2
-   *          The second primary name.
-   * @param oid2
-   *          The second oid.
-   * @param result
-   *          The expected result.
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test(dataProvider = "equalsTestData")
-  public void testEquals(String name1, String oid1, String name2,
-      String oid2, boolean result) throws Exception {
-    AttributeTypeBuilder builder1 = new AttributeTypeBuilder(name1,
-        oid1);
-    AttributeType type1 = builder1.getAttributeType();
-
-    AttributeTypeBuilder builder2 = new AttributeTypeBuilder(name2,
-        oid2);
-    AttributeType type2 = builder2.getAttributeType();
-
-    Assert.assertEquals(type1.equals(type2), result);
-    Assert.assertEquals(type2.equals(type1), result);
-  }
-
-  /**
-   * Check that the hasCode method operator works as expected.
-   * 
-   * @param name1
-   *          The first primary name.
-   * @param oid1
-   *          The first oid.
-   * @param name2
-   *          The second primary name.
-   * @param oid2
-   *          The second oid.
-   * @param result
-   *          The expected result.
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test(dataProvider = "equalsTestData")
-  public void testHashCode(String name1, String oid1, String name2,
-      String oid2, boolean result) throws Exception {
-    AttributeTypeBuilder builder1 = new AttributeTypeBuilder(name1,
-        oid1);
-    AttributeType type1 = builder1.getAttributeType();
-
-    AttributeTypeBuilder builder2 = new AttributeTypeBuilder(name2,
-        oid2);
-    AttributeType type2 = builder2.getAttributeType();
-
-    Assert.assertEquals(type1.hashCode() == type2.hashCode(), result);
-  }
 
   /**
    * Create test data for testing the
    * {@link AttributeType#generateHashCode(AttributeValue)} method.
-   * 
+   *
    * @return Returns the array of test data.
    */
   @DataProvider(name = "generateHashCodeTestData")
@@ -745,11 +548,13 @@
         { "one two", "onetwo", false }, { "one", "two", false } };
   }
 
+
+
   /**
    * Check that the
    * {@link AttributeType#generateHashCode(AttributeValue)} method
    * works as expected.
-   * 
+   *
    * @param value1
    *          The first test value.
    * @param value2
@@ -765,7 +570,7 @@
     AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
         "1.2.3");
     builder.setSyntax(DirectoryServer.getDefaultStringSyntax());
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     AttributeValue av1 = new AttributeValue(type, value1);
     AttributeValue av2 = new AttributeValue(type, value2);
@@ -776,10 +581,12 @@
     Assert.assertEquals(h1 == h2, result);
   }
 
+
+
   /**
    * Check that the {@link AttributeType#normalize(ByteString)} method
    * works as expected.
-   * 
+   *
    * @param value1
    *          The first test value.
    * @param value2
@@ -795,7 +602,7 @@
     AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
         "1.2.3");
     builder.setSyntax(DirectoryServer.getDefaultStringSyntax());
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     ByteString b1 = new ASN1OctetString(value1);
     ByteString b2 = new ASN1OctetString(value2);
@@ -806,325 +613,11 @@
     Assert.assertEquals(r1.equals(r2), result);
   }
 
-  /**
-   * Check that the {@link AttributeType#getDescription()} method
-   * returns <code>null</code> when there is no description.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetDescriptionDefault() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertNull(type.getDescription());
-  }
 
-  /**
-   * Check that the {@link AttributeType#getDescription()} method
-   * returns a description.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetDescription() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
-        "1.2.3");
-    builder.setDescription("hello");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getDescription(), "hello");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getExtraProperty(String)}
-   * method returns <code>null</code> when there is no property.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetExtraPropertyDefault() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertNull(type.getExtraProperty("test"));
-  }
-
-  /**
-   * Check that the {@link AttributeType#getExtraProperty(String)}
-   * method returns values.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetExtraProperty() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
-        "1.2.3");
-    String[] expectedValues = new String[] { "one", "two" };
-    builder.addExtraProperty("test", expectedValues);
-    AttributeType type = builder.getAttributeType();
-
-    Assert.assertNotNull(type.getExtraProperty("test"));
-    int i = 0;
-    for (String value : type.getExtraProperty("test")) {
-      Assert.assertEquals(value, expectedValues[i]);
-      i++;
-    }
-  }
-
-  /**
-   * Check that the {@link AttributeType#getExtraPropertyNames()}
-   * method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetExtraPropertyNames() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder("test",
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertFalse(type.getExtraPropertyNames().iterator()
-        .hasNext());
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNameOrOID()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNameOrOIDReturnsOID() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getNameOrOID(), "1.2.3");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNameOrOID()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNameOrOIDReturnsPrimaryName() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getNameOrOID(), "testType");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNameOrOID()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNameOrOIDReturnsOtherName() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    builder.addTypeNames("anotherName");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getNameOrOID(), "anotherName");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNormalizedNames()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNormalizedNames() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    builder.addTypeNames("anotherName", "yetAnotherName");
-    AttributeType type = builder.getAttributeType();
-
-    boolean gotTestType = false;
-    boolean gotAnotherName = false;
-    boolean gotYetAnotherName = false;
-
-    for (String name : type.getNormalizedNames()) {
-      if (name.equals("testtype")) {
-        gotTestType = true;
-      } else if (name.equals("anothername")) {
-        gotAnotherName = true;
-      } else if (name.equals("yetanothername")) {
-        gotYetAnotherName = true;
-      } else {
-        Assert.fail("Got unexpected normalized name: " + name);
-      }
-    }
-
-    Assert.assertTrue(gotTestType && gotAnotherName
-        && gotYetAnotherName);
-  }
-
-  /**
-   * Check that the {@link AttributeType#getUserDefinedNames()}
-   * method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetUserDefinedNames() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    builder.addTypeNames("anotherName", "yetAnotherName");
-    AttributeType type = builder.getAttributeType();
-
-    boolean gotTestType = false;
-    boolean gotAnotherName = false;
-    boolean gotYetAnotherName = false;
-
-    for (String name : type.getUserDefinedNames()) {
-      if (name.equals("testType")) {
-        gotTestType = true;
-      } else if (name.equals("anotherName")) {
-        gotAnotherName = true;
-      } else if (name.equals("yetAnotherName")) {
-        gotYetAnotherName = true;
-      } else {
-        Assert.fail("Got unexpected user defined name: " + name);
-      }
-    }
-
-    Assert.assertTrue(gotTestType && gotAnotherName
-        && gotYetAnotherName);
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNormalizedPrimaryName()}
-   * method returns <code>null</code> when there is no primary name.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNormalizedPrimaryNameDefault() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertNull(type.getNormalizedPrimaryName());
-  }
-
-  /**
-   * Check that the {@link AttributeType#getNormalizedPrimaryName()}
-   * method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetNormalizedPrimaryName() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getNormalizedPrimaryName(), "testtype");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getOID()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetOID() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getOID(), "1.2.3");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getPrimaryName()} method
-   * returns <code>null</code> when there is no primary name.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetPrimaryNameDefault() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertNull(type.getPrimaryName());
-  }
-
-  /**
-   * Check that the {@link AttributeType#getPrimaryName()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetPrimaryName() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getPrimaryName(), "testType");
-  }
-
-  /**
-   * Check that the {@link AttributeType#getSchemaFile()} method
-   * returns <code>null</code> when there is no schema file.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetSchemaFileDefault() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertNull(type.getSchemaFile());
-  }
-
-  /**
-   * Check that the {@link AttributeType#getSchemaFile()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testGetSchemaFile() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
-        "1.2.3");
-    builder.addExtraProperty(
-        ServerConstants.SCHEMA_PROPERTY_FILENAME, "/foo/bar");
-    AttributeType type = builder.getAttributeType();
-    Assert.assertEquals(type.getSchemaFile(), "/foo/bar");
-  }
-
-  /**
-   * Check that the {@link AttributeType#hasNameOrOID(String)} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testHasNameOrOID() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
-
-    Assert.assertTrue(type.hasNameOrOID("testtype"));
-    Assert.assertTrue(type.hasNameOrOID("1.2.3"));
-    Assert.assertFalse(type.hasNameOrOID("x.y.z"));
-  }
 
   /**
    * Check that the {@link AttributeType#isCollective()} method.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -1132,21 +625,23 @@
   public void testIsCollective() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertFalse(type.isCollective());
 
     builder = new AttributeTypeBuilder("testType", "1.2.3");
     builder.setCollective(true);
-    type = builder.getAttributeType();
+    type = builder.getInstance();
 
     Assert.assertTrue(type.isCollective());
   }
 
+
+
   /**
    * Check that the {@link AttributeType#isNoUserModification()}
    * method.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -1154,41 +649,22 @@
   public void testIsNoUserModification() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertFalse(type.isNoUserModification());
 
     builder = new AttributeTypeBuilder("testType", "1.2.3");
     builder.setNoUserModification(true);
-    type = builder.getAttributeType();
+    type = builder.getInstance();
 
     Assert.assertTrue(type.isNoUserModification());
   }
 
-  /**
-   * Check that the {@link AttributeType#isObsolete()} method.
-   * 
-   * @throws Exception
-   *           If the test failed unexpectedly.
-   */
-  @Test
-  public void testIsObsolete() throws Exception {
-    AttributeTypeBuilder builder = new AttributeTypeBuilder(
-        "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
 
-    Assert.assertFalse(type.isObsolete());
-
-    builder = new AttributeTypeBuilder("testType", "1.2.3");
-    builder.setObsolete(true);
-    type = builder.getAttributeType();
-
-    Assert.assertTrue(type.isObsolete());
-  }
 
   /**
    * Check that the {@link AttributeType#isSingleValue()} method.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -1196,21 +672,23 @@
   public void testIsSingleValue() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
 
     Assert.assertFalse(type.isSingleValue());
 
     builder = new AttributeTypeBuilder("testType", "1.2.3");
     builder.setSingleValue(true);
-    type = builder.getAttributeType();
+    type = builder.getInstance();
 
     Assert.assertTrue(type.isSingleValue());
   }
 
+
+
   /**
    * Create test data for testing the
    * {@link AttributeType#isOperational()} method.
-   * 
+   *
    * @return Returns the array of test data.
    */
   @DataProvider(name = "isOperationalTestData")
@@ -1222,9 +700,11 @@
         { AttributeUsage.DSA_OPERATION, true } };
   }
 
+
+
   /**
    * Check that the {@link AttributeType#isOperational()} method.
-   * 
+   *
    * @param usage
    *          The attribute usage.
    * @param result
@@ -1238,13 +718,15 @@
     AttributeTypeBuilder builder = new AttributeTypeBuilder(
         "testType", "1.2.3");
     builder.setAttributeUsage(usage);
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
     Assert.assertEquals(type.isOperational(), result);
   }
 
+
+
   /**
    * Check that the {@link AttributeType#toString()} method.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -1252,7 +734,7 @@
   public void testToStringDefault() throws Exception {
     AttributeTypeBuilder builder = new AttributeTypeBuilder(null,
         "1.2.3");
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
     Assert.assertEquals(type.toString(), "( 1.2.3 "
         + "EQUALITY caseIgnoreMatch "
         + "ORDERING caseIgnoreOrderingMatch "
@@ -1261,9 +743,11 @@
         + "USAGE userApplications )");
   }
 
+
+
   /**
    * Check that the {@link AttributeType#toString()} method.
-   * 
+   *
    * @throws Exception
    *           If the test failed unexpectedly.
    */
@@ -1278,11 +762,21 @@
     builder.addExtraProperty(
         ServerConstants.SCHEMA_PROPERTY_FILENAME, "/foo/bar");
 
-    AttributeType type = builder.getAttributeType();
+    AttributeType type = builder.getInstance();
     Assert.assertEquals(type.toString(), "( 1.2.3 "
-        + "NAME ( 'anotherName' 'testType' ) "
+        + "NAME ( 'testType' 'anotherName' ) "
         + "EQUALITY booleanMatch "
         + "SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 " + "SINGLE-VALUE "
         + "USAGE directoryOperation " + "X-SCHEMA-FILE '/foo/bar' )");
   }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected SchemaDefinitionBuilder getBuilder(String name, String oid) {
+    return new AttributeTypeBuilder(name, oid);
+  }
 }
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestCommonSchemaElements.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestCommonSchemaElements.java
new file mode 100644
index 0000000..6400d29
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestCommonSchemaElements.java
@@ -0,0 +1,789 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.opends.server.TestCaseUtils;
+import org.opends.server.util.ServerConstants;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+
+
+/**
+ * This class defines a set of tests for the
+ * {@link org.opends.server.types.CommonSchemaElements} class and
+ * derived classes.
+ */
+public abstract class TestCommonSchemaElements extends TypesTestCase {
+  /**
+   * Internal class to simplify construction of attribute types.
+   *
+   * @param <T>
+   *          The type of definition that this builder constructs.
+   */
+  protected static abstract class SchemaDefinitionBuilder<T extends CommonSchemaElements> {
+    // The primary name to use for this attribute type.
+    private String primaryName;
+
+    // The set of names for this attribute type.
+    private List<String> names;
+
+    // The OID that may be used to reference this attribute type.
+    private String oid;
+
+    // The description for this attribute type.
+    private String description;
+
+    // Indicates whether this attribute type is declared "obsolete".
+    private boolean isObsolete;
+
+    // The set of additional name-value pairs associated with this
+    // attribute type definition.
+    private Map<String, List<String>> extraProperties;
+
+
+
+    // Reset the builder to its initial state.
+    private void reset() {
+      this.primaryName = null;
+      this.names = null;
+      this.oid = null;
+      this.description = null;
+      this.isObsolete = false;
+      this.extraProperties = null;
+
+      resetBuilder();
+    }
+
+
+
+    /**
+     * Create a new attribute type builder.
+     */
+    protected SchemaDefinitionBuilder() {
+      reset();
+    }
+
+
+
+    /**
+     * Create a new attribute type builder.
+     *
+     * @param primaryName
+     *          The attribute type primary name.
+     * @param oid
+     *          The attribute type OID.
+     */
+    protected SchemaDefinitionBuilder(String primaryName, String oid) {
+      reset();
+
+      this.primaryName = primaryName;
+      this.oid = oid;
+    }
+
+
+
+    /**
+     * Construct an attribute type based on the properties of the
+     * builder.
+     *
+     * @return The new attribute type.
+     */
+    public final T getInstance() {
+      if (oid == null) {
+        throw new IllegalStateException("Null OID.");
+      }
+
+      T instance = buildInstance(primaryName, names, oid,
+          description, isObsolete, extraProperties);
+
+      // Reset the internal state.
+      reset();
+
+      return instance;
+    }
+
+
+
+    /**
+     * Build a new instance using this builder.
+     *
+     * @param primaryName
+     *          The primary name.
+     * @param names
+     *          The optional names.
+     * @param oid
+     *          The OID.
+     * @param description
+     *          The optional description.
+     * @param isObsolete
+     *          Whether or not the definition is obsolete.
+     * @param extraProperties
+     *          The extra properties.
+     * @return Returns the newly constructed definition.
+     */
+    protected abstract T buildInstance(String primaryName,
+        Collection<String> names, String oid, String description,
+        boolean isObsolete, Map<String, List<String>> extraProperties);
+
+
+
+    /**
+     * Reset the internal state of the builder.
+     */
+    protected abstract void resetBuilder();
+
+
+
+    /**
+     * Set the description.
+     *
+     * @param description
+     *          The description.
+     */
+    public final void setDescription(String description) {
+      this.description = description;
+    }
+
+
+
+    /**
+     * Add extra property value(s).
+     *
+     * @param name
+     *          The name of the extra property.
+     * @param values
+     *          The value(s) of the extra property.
+     */
+    public final void addExtraProperty(String name, String... values) {
+      if (name == null) {
+        throw new NullPointerException("Null extra property name");
+      }
+
+      if (values == null) {
+        throw new NullPointerException("Null extra property values");
+      }
+
+      if (extraProperties == null) {
+        extraProperties = new HashMap<String, List<String>>();
+      }
+
+      List<String> l = extraProperties.get(name);
+      if (l == null) {
+        l = new ArrayList<String>();
+        extraProperties.put(name, l);
+      }
+      l.addAll(Arrays.asList(values));
+    }
+
+
+
+    /**
+     * Set the isObsolete.
+     *
+     * @param isObsolete
+     *          The isObsolete.
+     */
+    public final void setObsolete(boolean isObsolete) {
+      this.isObsolete = isObsolete;
+    }
+
+
+
+    /**
+     * Set the oid.
+     *
+     * @param oid
+     *          The oid.
+     */
+    public final void setOid(String oid) {
+      if (oid == null) {
+        throw new NullPointerException("Null OID");
+      }
+
+      this.oid = oid;
+    }
+
+
+
+    /**
+     * Set the primaryName.
+     *
+     * @param primaryName
+     *          The primaryName.
+     */
+    public final void setPrimaryName(String primaryName) {
+      this.primaryName = primaryName;
+    }
+
+
+
+    /**
+     * Add attribute type name(s).
+     *
+     * @param names
+     *          The attribute type name(s) to add.
+     */
+    public final void addTypeNames(String... names) {
+      if (names == null) {
+        throw new NullPointerException("Null names");
+      }
+
+      if (this.names == null) {
+        this.names = new LinkedList<String>();
+      }
+
+      this.names.addAll(Arrays.asList(names));
+    }
+  }
+
+
+
+  /**
+   * Create a new schema definition builder.
+   *
+   * @param name
+   *          The schema definition's primary name.
+   * @param oid
+   *          The OID of the schema definition.
+   * @return The new builder.
+   */
+  protected abstract SchemaDefinitionBuilder getBuilder(String name,
+      String oid);
+
+
+
+  /**
+   * Once-only initialization.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @BeforeClass
+  public final void setUp() throws Exception {
+    // This test suite depends on having the schema available, so
+    // we'll
+    // start the server.
+    TestCaseUtils.startServer();
+  }
+
+
+
+  /**
+   * Check that the primary name is added to the set of names.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testConstructorPrimaryName() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+
+    Assert.assertTrue(d.hasName("testtype"));
+    Assert.assertFalse(d.hasName("xxx"));
+  }
+
+
+
+  /**
+   * Check that the type names are accessible.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testConstructorTypeNames() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+
+    builder.addTypeNames("testNameAlias", "anotherNameAlias");
+    CommonSchemaElements d = builder.getInstance();
+
+    Assert.assertTrue(d.hasName("testtype"));
+    Assert.assertTrue(d.hasName("testnamealias"));
+    Assert.assertTrue(d.hasName("anothernamealias"));
+  }
+
+
+
+  /**
+   * Create test data for testing the
+   * {@link CommonSchemaElements#equals(Object)} method.
+   *
+   * @return Returns the array of test data.
+   */
+  @DataProvider(name = "equalsTestData")
+  public final Object[][] createEqualsTestData() {
+    return new Object[][] {
+        { "testType", "1.2.3", "testType", "1.2.3", true },
+        { "testType", "1.2.3", "xxx", "1.2.3", true },
+        { "testType", "1.2.3", "testType", "1.2.4", false },
+        { "testType", "1.2.3", "xxx", "1.2.4", false } };
+  }
+
+
+
+  /**
+   * Check that the equals operator works as expected.
+   *
+   * @param name1
+   *          The first primary name.
+   * @param oid1
+   *          The first oid.
+   * @param name2
+   *          The second primary name.
+   * @param oid2
+   *          The second oid.
+   * @param result
+   *          The expected result.
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dataProvider = "equalsTestData")
+  public final void testEquals(String name1, String oid1,
+      String name2, String oid2, boolean result) throws Exception {
+    SchemaDefinitionBuilder builder1 = getBuilder(name1, oid1);
+    CommonSchemaElements d1 = builder1.getInstance();
+
+    SchemaDefinitionBuilder builder2 = getBuilder(name2, oid2);
+    CommonSchemaElements d2 = builder2.getInstance();
+
+    Assert.assertEquals(d1.equals(d2), result);
+    Assert.assertEquals(d2.equals(d1), result);
+  }
+
+
+
+  /**
+   * Check that the hasCode method operator works as expected.
+   *
+   * @param name1
+   *          The first primary name.
+   * @param oid1
+   *          The first oid.
+   * @param name2
+   *          The second primary name.
+   * @param oid2
+   *          The second oid.
+   * @param result
+   *          The expected result.
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dataProvider = "equalsTestData")
+  public final void testHashCode(String name1, String oid1,
+      String name2, String oid2, boolean result) throws Exception {
+    SchemaDefinitionBuilder builder1 = getBuilder(name1, oid1);
+    CommonSchemaElements d1 = builder1.getInstance();
+
+    SchemaDefinitionBuilder builder2 = getBuilder(name2, oid2);
+    CommonSchemaElements d2 = builder2.getInstance();
+
+    Assert.assertEquals(d1.hashCode() == d2.hashCode(), result);
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getDescription()}
+   * method returns <code>null</code> when there is no description.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetDescriptionDefault() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("test", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertNull(d.getDescription());
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getDescription()}
+   * method returns a description.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetDescription() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("test", "1.2.3");
+    builder.setDescription("hello");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getDescription(), "hello");
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link CommonSchemaElements#getExtraProperty(String)} method
+   * returns <code>null</code> when there is no property.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetExtraPropertyDefault() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("test", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertNull(d.getExtraProperty("test"));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link CommonSchemaElements#getExtraProperty(String)} method
+   * returns values.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetExtraProperty() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("test", "1.2.3");
+    String[] expectedValues = new String[] { "one", "two" };
+    builder.addExtraProperty("test", expectedValues);
+    CommonSchemaElements d = builder.getInstance();
+
+    Assert.assertNotNull(d.getExtraProperty("test"));
+    int i = 0;
+    for (String value : d.getExtraProperty("test")) {
+      Assert.assertEquals(value, expectedValues[i]);
+      i++;
+    }
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link CommonSchemaElements#getExtraPropertyNames()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetExtraPropertyNames() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("test", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert
+        .assertFalse(d.getExtraPropertyNames().iterator().hasNext());
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getNameOrOID()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNameOrOIDReturnsOID() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getNameOrOID(), "1.2.3");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getNameOrOID()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNameOrOIDReturnsPrimaryName()
+      throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getNameOrOID(), "testType");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getNameOrOID()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNameOrOIDReturnsOtherName()
+      throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    builder.addTypeNames("anotherName");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getNameOrOID(), "anotherName");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getNormalizedNames()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNormalizedNames() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    builder.addTypeNames("anotherName", "yetAnotherName");
+    CommonSchemaElements d = builder.getInstance();
+
+    boolean gotTestType = false;
+    boolean gotAnotherName = false;
+    boolean gotYetAnotherName = false;
+
+    for (String name : d.getNormalizedNames()) {
+      if (name.equals("testtype")) {
+        gotTestType = true;
+      } else if (name.equals("anothername")) {
+        gotAnotherName = true;
+      } else if (name.equals("yetanothername")) {
+        gotYetAnotherName = true;
+      } else {
+        Assert.fail("Got unexpected normalized name: " + name);
+      }
+    }
+
+    Assert.assertTrue(gotTestType && gotAnotherName
+        && gotYetAnotherName);
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getUserDefinedNames()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetUserDefinedNames() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    builder.addTypeNames("anotherName", "yetAnotherName");
+    CommonSchemaElements d = builder.getInstance();
+
+    boolean gotTestType = false;
+    boolean gotAnotherName = false;
+    boolean gotYetAnotherName = false;
+
+    for (String name : d.getUserDefinedNames()) {
+      if (name.equals("testType")) {
+        gotTestType = true;
+      } else if (name.equals("anotherName")) {
+        gotAnotherName = true;
+      } else if (name.equals("yetAnotherName")) {
+        gotYetAnotherName = true;
+      } else {
+        Assert.fail("Got unexpected user defined name: " + name);
+      }
+    }
+
+    Assert.assertTrue(gotTestType && gotAnotherName
+        && gotYetAnotherName);
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link CommonSchemaElements#getNormalizedPrimaryName()} method
+   * returns <code>null</code> when there is no primary name.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNormalizedPrimaryNameDefault()
+      throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertNull(d.getNormalizedPrimaryName());
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link CommonSchemaElements#getNormalizedPrimaryName()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetNormalizedPrimaryName() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getNormalizedPrimaryName(), "testtype");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getOID()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetOID() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getOID(), "1.2.3");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getPrimaryName()}
+   * method returns <code>null</code> when there is no primary name.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetPrimaryNameDefault() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertNull(d.getPrimaryName());
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getPrimaryName()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetPrimaryName() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getPrimaryName(), "testType");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getSchemaFile()}
+   * method returns <code>null</code> when there is no schema file.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetSchemaFileDefault() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertNull(d.getSchemaFile());
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#getSchemaFile()}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testGetSchemaFile() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder(null, "1.2.3");
+    builder.addExtraProperty(
+        ServerConstants.SCHEMA_PROPERTY_FILENAME, "/foo/bar");
+    CommonSchemaElements d = builder.getInstance();
+    Assert.assertEquals(d.getSchemaFile(), "/foo/bar");
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#hasNameOrOID(String)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testHasNameOrOID() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+
+    Assert.assertTrue(d.hasNameOrOID("testtype"));
+    Assert.assertTrue(d.hasNameOrOID("1.2.3"));
+    Assert.assertFalse(d.hasNameOrOID("x.y.z"));
+  }
+
+
+
+  /**
+   * Check that the {@link CommonSchemaElements#isObsolete()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public final void testIsObsolete() throws Exception {
+    SchemaDefinitionBuilder builder = getBuilder("testType", "1.2.3");
+    CommonSchemaElements d = builder.getInstance();
+
+    Assert.assertFalse(d.isObsolete());
+
+    builder = getBuilder("testType", "1.2.3");
+    builder.setObsolete(true);
+    d = builder.getInstance();
+
+    Assert.assertTrue(d.isObsolete());
+  }
+}
diff --git a/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java
new file mode 100644
index 0000000..41fc1da
--- /dev/null
+++ b/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java
@@ -0,0 +1,1237 @@
+/*
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE
+ * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
+ * 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
+ * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  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
+ *
+ *
+ *      Portions Copyright 2006 Sun Microsystems, Inc.
+ */
+package org.opends.server.types;
+
+
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.opends.server.core.DirectoryServer;
+import org.opends.server.util.ServerConstants;
+import org.testng.Assert;
+import org.testng.annotations.BeforeClass;
+import org.testng.annotations.DataProvider;
+import org.testng.annotations.Test;
+
+
+
+/**
+ * This class defines a set of tests for the
+ * {@link org.opends.server.types.ObjectClass} class.
+ */
+public final class TestObjectClass extends TestCommonSchemaElements {
+  /**
+   * Internal class to simplify construction of object classes.
+   */
+  private static final class ObjectClassBuilder extends
+      SchemaDefinitionBuilder<ObjectClass> {
+    // The superior object class from which this object class
+    // inherits.
+    private ObjectClass superior;
+
+    // The type of object class.
+    private ObjectClassType objectClassType;
+
+    // The set of required attribute types.
+    private Set<AttributeType> requiredAttributeTypes;
+
+    // The set of optional attribute types.
+    private Set<AttributeType> optionalAttributeTypes;
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    protected void resetBuilder() {
+      this.superior = null;
+      this.objectClassType = ObjectClassType.STRUCTURAL;
+      this.requiredAttributeTypes = null;
+      this.optionalAttributeTypes = null;
+    }
+
+
+
+    /**
+     * Create a new object class builder.
+     */
+    public ObjectClassBuilder() {
+      super();
+    }
+
+
+
+    /**
+     * Create a new object class builder.
+     *
+     * @param primaryName
+     *          The object class primary name.
+     * @param oid
+     *          The object class OID.
+     */
+    public ObjectClassBuilder(String primaryName, String oid) {
+      super(primaryName, oid);
+    }
+
+
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected ObjectClass buildInstance(String primaryName,
+        Collection<String> names, String oid, String description,
+        boolean isObsolete, Map<String, List<String>> extraProperties) {
+      return new ObjectClass(primaryName, names, oid, description,
+          superior, requiredAttributeTypes, optionalAttributeTypes,
+          objectClassType, isObsolete, extraProperties);
+    }
+
+
+
+    /**
+     * Set the objectClassType.
+     *
+     * @param objectClassType
+     *          The objectClassType.
+     */
+    public void setObjectClassType(ObjectClassType objectClassType) {
+      this.objectClassType = objectClassType;
+    }
+
+
+
+    /**
+     * Set the superior.
+     *
+     * @param superior
+     *          The superior.
+     */
+    public void setSuperior(ObjectClass superior) {
+      this.superior = superior;
+    }
+
+
+
+    /**
+     * Add required attribute types.
+     *
+     * @param types
+     *          The required attribute type(s) to add.
+     */
+    public void addRequiredAttributeTypes(AttributeType... types) {
+      if (types == null) {
+        throw new NullPointerException("Null types");
+      }
+
+      if (this.requiredAttributeTypes == null) {
+        this.requiredAttributeTypes = new LinkedHashSet<AttributeType>();
+      }
+
+      this.requiredAttributeTypes.addAll(Arrays.asList(types));
+    }
+
+
+
+    /**
+     * Add optional attribute types.
+     *
+     * @param types
+     *          The optional attribute type(s) to add.
+     */
+    public void addOptionalAttributeTypes(AttributeType... types) {
+      if (types == null) {
+        throw new NullPointerException("Null types");
+      }
+
+      if (this.optionalAttributeTypes == null) {
+        this.optionalAttributeTypes = new LinkedHashSet<AttributeType>();
+      }
+
+      this.optionalAttributeTypes.addAll(Arrays.asList(types));
+    }
+  }
+
+
+
+  // Array of attribute types to use in tests.
+  private AttributeType[] types;
+
+
+
+  /**
+   * Once-only initialization.
+   *
+   * @throws Exception
+   *           If an unexpected error occurred.
+   */
+  @BeforeClass(dependsOnMethods = "setUp")
+  public final void setUpTypes() throws Exception {
+    types = new AttributeType[10];
+
+    for (int i = 0; i < types.length; i++) {
+      String name = "testType" + i;
+      types[i] = DirectoryServer.getDefaultAttributeType(name);
+    }
+  }
+
+
+
+  /**
+   * Check that the constructor throws an NPE when mandatory
+   * parameters are not specified.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(expectedExceptions = NullPointerException.class)
+  public void testConstructorNPE() throws Exception {
+    Set<AttributeType> emptySet = Collections.emptySet();
+    Map<String, List<String>> emptyMap = Collections.emptyMap();
+
+    new ObjectClass("test", Collections.singleton("test"), null,
+        "description", DirectoryServer.getTopObjectClass(), emptySet,
+        emptySet, ObjectClassType.STRUCTURAL, false, emptyMap);
+  }
+
+
+
+  /**
+   * Check that the constructor does not throw an exception when all
+   * optional parameters are not specified.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testConstructorDefault() throws Exception {
+    ObjectClass type = new ObjectClass(null, null, "1.2.3", null,
+        null, null, null, null, false, null);
+
+    Assert.assertNull(type.getPrimaryName());
+  }
+
+
+
+  /**
+   * Create test data for testing the
+   * {@link AttributeType#isOperational()} method.
+   *
+   * @return Returns the array of test data.
+   */
+  @DataProvider(name = "getObjectClassTypeTestData")
+  public Object[][] createGetObjectClassTypeTestData() {
+    return new Object[][] { { null, ObjectClassType.STRUCTURAL },
+        { ObjectClassType.STRUCTURAL, ObjectClassType.STRUCTURAL },
+        { ObjectClassType.ABSTRACT, ObjectClassType.ABSTRACT },
+        { ObjectClassType.AUXILIARY, ObjectClassType.AUXILIARY } };
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#getObjectClassType()} method.
+   *
+   * @param type
+   *          The object class type.
+   * @param result
+   *          Expected result.
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dataProvider = "getObjectClassTypeTestData")
+  public void testGetObjectClassType(ObjectClassType type,
+      ObjectClassType result) throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    builder.setObjectClassType(type);
+    ObjectClass c = builder.getInstance();
+    Assert.assertEquals(c.getObjectClassType(), result);
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributeChain()} method
+   * with no superior and no optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributeChainNoSuperiorEmpty()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.getOptionalAttributeChain().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributeChain()} method
+   * with no superior and some optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributeChainNoSuperior()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass c = builder.getInstance();
+
+    Set<AttributeType> chain = c.getOptionalAttributeChain();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributeChain()} method
+   * with a superior but no optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributeChainEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getOptionalAttributeChain();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributeChain()} method
+   * with a superior and some optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributeChain() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addOptionalAttributeTypes(types[3], types[4], types[5]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getOptionalAttributeChain();
+    Assert.assertEquals(chain.size(), 6);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+    Assert.assertTrue(chain.contains(types[3]));
+    Assert.assertTrue(chain.contains(types[4]));
+    Assert.assertTrue(chain.contains(types[5]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributes()} method with
+   * no superior and no optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributesNoSuperiorEmpty()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.getOptionalAttributes().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributes()} method with
+   * no superior and some optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributesNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass c = builder.getInstance();
+
+    Set<AttributeType> chain = c.getOptionalAttributes();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributes()} method with
+   * a superior but no optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributesEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.getOptionalAttributes().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getOptionalAttributes()} method with
+   * a superior and some optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetOptionalAttributes() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addOptionalAttributeTypes(types[3], types[4], types[5]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getOptionalAttributes();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[3]));
+    Assert.assertTrue(chain.contains(types[4]));
+    Assert.assertTrue(chain.contains(types[5]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributeChain()} method
+   * with no superior and no optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributeChainNoSuperiorEmpty()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.getRequiredAttributeChain().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributeChain()} method
+   * with no superior and some optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributeChainNoSuperior()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass c = builder.getInstance();
+
+    Set<AttributeType> chain = c.getRequiredAttributeChain();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributeChain()} method
+   * with a superior but no optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributeChainEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getRequiredAttributeChain();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributeChain()} method
+   * with a superior and some optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributeChain() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addRequiredAttributeTypes(types[3], types[4], types[5]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getRequiredAttributeChain();
+    Assert.assertEquals(chain.size(), 6);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+    Assert.assertTrue(chain.contains(types[3]));
+    Assert.assertTrue(chain.contains(types[4]));
+    Assert.assertTrue(chain.contains(types[5]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributes()} method with
+   * no superior and no optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributesNoSuperiorEmpty()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.getRequiredAttributes().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributes()} method with
+   * no superior and some optional attributes.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributesNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass c = builder.getInstance();
+
+    Set<AttributeType> chain = c.getRequiredAttributes();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[0]));
+    Assert.assertTrue(chain.contains(types[1]));
+    Assert.assertTrue(chain.contains(types[2]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributes()} method with
+   * a superior but no optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributesEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.getRequiredAttributes().isEmpty());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getRequiredAttributes()} method with
+   * a superior and some optional attributes of its own.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetRequiredAttributes() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addRequiredAttributeTypes(types[3], types[4], types[5]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Set<AttributeType> chain = child.getRequiredAttributes();
+    Assert.assertEquals(chain.size(), 3);
+    Assert.assertTrue(chain.contains(types[3]));
+    Assert.assertTrue(chain.contains(types[4]));
+    Assert.assertTrue(chain.contains(types[5]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getSuperiorClass()} method with no
+   * superior.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetSuperiorClassNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertNull(c.getSuperiorClass());
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#getSuperiorClass()} method with a
+   * superior.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testGetSuperiorClassWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertEquals(child.getSuperiorClass(), parent);
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#isDescendantOf(ObjectClass)} method
+   * with no superior.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsDescendantOfNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("testType1",
+        "1.2.1");
+    ObjectClass c1 = builder.getInstance();
+
+    builder = new ObjectClassBuilder("testType2", "1.2.2");
+    ObjectClass c2 = builder.getInstance();
+
+    Assert.assertFalse(c1.isDescendantOf(c2));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#isDescendantOf(ObjectClass)} method
+   * with a superior.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsDescendantOfWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "grandParent", "1.2.1");
+    ObjectClass grandParent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("parent", "1.2.2");
+    builder.setSuperior(grandParent);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(parent.isDescendantOf(grandParent));
+    Assert.assertTrue(child.isDescendantOf(parent));
+    Assert.assertTrue(child.isDescendantOf(grandParent));
+
+    Assert.assertFalse(child.isDescendantOf(child));
+    Assert.assertFalse(parent.isDescendantOf(child));
+    Assert.assertFalse(grandParent.isDescendantOf(child));
+  }
+
+
+
+  /**
+   * Create test data for testing the
+   * {@link ObjectClass#isExtensibleObject()} method.
+   *
+   * @return Returns the array of test data.
+   */
+  @DataProvider(name = "isExtensibleObjectTestData")
+  public Object[][] createIsExtensibleObjectTestData() {
+    return new Object[][] { { "test", "1.2.3", false },
+        { "extensibleObject", "1.2.3", true },
+        { "test", "1.3.6.1.4.1.1466.101.120.111", true },
+        { "extensibleObject", "1.3.6.1.4.1.1466.101.120.111", true } };
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#getObjectClassType()} method.
+   *
+   * @param name
+   *          The object class name.
+   * @param oid
+   *          The object class oid.
+   * @param result
+   *          Expected result.
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dataProvider = "isExtensibleObjectTestData")
+  public void testIsExtensibleObject(String name, String oid,
+      boolean result) throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(name, oid);
+    ObjectClass c = builder.getInstance();
+    Assert.assertEquals(c.isExtensibleObject(), result);
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsOptionalEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertFalse(c.isOptional(types[0]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsOptionalNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isOptional(types[0]));
+    Assert.assertFalse(c.isOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsOptionalEmptyWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isOptional(types[0]));
+    Assert.assertFalse(child.isOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsOptionalWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addOptionalAttributeTypes(types[1]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isOptional(types[0]));
+    Assert.assertTrue(child.isOptional(types[1]));
+    Assert.assertFalse(child.isOptional(types[2]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsOptionalExtensible() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "extensibleObject", "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isOptional(types[0]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsOptionalExtensibleRequired() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "extensibleObject", "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass c = builder.getInstance();
+    Assert.assertFalse(c.isOptional(types[0]));
+    Assert.assertTrue(c.isOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isOptional(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsOptionalExtensibleRequiredSuperior()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("extensibleObject", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass c = builder.getInstance();
+
+    Assert.assertFalse(c.isOptional(types[0]));
+    Assert.assertTrue(c.isOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isRequired(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertFalse(c.isRequired(types[0]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isRequired(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isRequired(types[0]));
+    Assert.assertFalse(c.isRequired(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isRequired(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredEmptyWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isRequired(types[0]));
+    Assert.assertFalse(child.isRequired(types[1]));
+  }
+
+
+
+  /**
+   * Check that the {@link ObjectClass#isRequired(AttributeType)}
+   * method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addRequiredAttributeTypes(types[1]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isRequired(types[0]));
+    Assert.assertTrue(child.isRequired(types[1]));
+    Assert.assertFalse(child.isRequired(types[2]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredOrOptionalEmpty() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertFalse(c.isRequiredOrOptional(types[0]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredOrOptionalNoSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("test",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    builder.addRequiredAttributeTypes(types[1]);
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isRequiredOrOptional(types[0]));
+    Assert.assertTrue(c.isRequiredOrOptional(types[1]));
+    Assert.assertFalse(c.isRequiredOrOptional(types[2]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredOrOptionalEmptyWithSuperior()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    builder.addRequiredAttributeTypes(types[1]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isRequiredOrOptional(types[0]));
+    Assert.assertTrue(child.isRequiredOrOptional(types[1]));
+    Assert.assertFalse(child.isRequiredOrOptional(types[2]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testIsRequiredOrOptionalWithSuperior() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addOptionalAttributeTypes(types[0]);
+    builder.addRequiredAttributeTypes(types[1]);
+    ObjectClass parent = builder.getInstance();
+    builder = new ObjectClassBuilder("child", "1.2.3");
+    builder.addOptionalAttributeTypes(types[2]);
+    builder.addRequiredAttributeTypes(types[3]);
+    builder.setSuperior(parent);
+    ObjectClass child = builder.getInstance();
+
+    Assert.assertTrue(child.isRequiredOrOptional(types[0]));
+    Assert.assertTrue(child.isRequiredOrOptional(types[1]));
+    Assert.assertTrue(child.isRequiredOrOptional(types[2]));
+    Assert.assertTrue(child.isRequiredOrOptional(types[3]));
+    Assert.assertFalse(child.isRequiredOrOptional(types[4]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsRequiredOrOptionalExtensible() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "extensibleObject", "1.2.3");
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isRequiredOrOptional(types[0]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsRequiredOrOptionalExtensibleRequired()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "extensibleObject", "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass c = builder.getInstance();
+    Assert.assertTrue(c.isRequiredOrOptional(types[0]));
+    Assert.assertTrue(c.isRequiredOrOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check that the
+   * {@link ObjectClass#isRequiredOrOptional(AttributeType)} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test(dependsOnMethods = "testIsExtensibleObject")
+  public void testIsRequiredOrOptionalExtensibleRequiredSuperior()
+      throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder("parent",
+        "1.2.3");
+    builder.addRequiredAttributeTypes(types[0]);
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("extensibleObject", "1.2.3");
+    builder.setSuperior(parent);
+    ObjectClass c = builder.getInstance();
+
+    Assert.assertTrue(c.isRequiredOrOptional(types[0]));
+    Assert.assertTrue(c.isRequiredOrOptional(types[1]));
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#toString()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testToStringDefault() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(null, "1.2.3");
+    ObjectClass type = builder.getInstance();
+    Assert.assertEquals(type.toString(), "( 1.2.3 STRUCTURAL )");
+  }
+
+
+
+  /**
+   * Check the {@link ObjectClass#toString()} method.
+   *
+   * @throws Exception
+   *           If the test failed unexpectedly.
+   */
+  @Test
+  public void testToString() throws Exception {
+    ObjectClassBuilder builder = new ObjectClassBuilder(
+        "parentClass", "1.2.1");
+    ObjectClass parent = builder.getInstance();
+
+    builder = new ObjectClassBuilder("childClass", "1.2.2");
+    builder.addTypeNames("anotherName");
+    builder.setDescription("A description");
+    builder.setObjectClassType(ObjectClassType.ABSTRACT);
+    builder.setObsolete(true);
+    builder.setSuperior(parent);
+    builder.addRequiredAttributeTypes(types[0], types[1], types[2]);
+    builder.addOptionalAttributeTypes(types[3]);
+    builder.addExtraProperty(
+        ServerConstants.SCHEMA_PROPERTY_FILENAME, "/foo/bar");
+    ObjectClass type = builder.getInstance();
+    Assert.assertEquals(type.toString(), "( 1.2.2 "
+        + "NAME ( 'childClass' 'anotherName' ) "
+        + "DESC 'A description' " + "OBSOLETE " + "SUP parentClass "
+        + "ABSTRACT " + "MUST ( testType0 $ testType1 $ testType2 ) "
+        + "MAY testType3 " + "X-SCHEMA-FILE '/foo/bar' )");
+  }
+
+
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected SchemaDefinitionBuilder getBuilder(String name, String oid) {
+    return new ObjectClassBuilder(name, oid);
+  }
+
+}

--
Gitblit v1.10.0