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

neil_a_wilson
09.20.2007 bdf67a3c15262fb750ae1c0302212dc03ff94a06
Update the schema backend to provide full support for online schema updates.
which includes the following:

- It is now possible to add attribute types, objectclasses, name forms, DIT
content rules, DIT structure rules, and matching rule uses. Adding a schema
element that is already present but with a different definition will replace
that element.

- It is now also possible to remove existing attribute types, objectclasses,
name forms, DIT content rules, DIT structure rules, and matching rule uses.
If the element is removed and then re-added, then it is treated as adding the
element with a a different definition (i.e., replacing the existing
definition).

- If an existing schema element is replaced or removed, then the update will be
made in the schema file that contains that definition. If a new attribute
type is added and is not manually associated with a schema file, then the
server will use a default schema file of 99-user.ldif. The schema file to
use can be manually overridden using the X-SCHEMA-FILE extension.

- A significant amount of effort has been put into understanding dependencies
between various schema elements and ensuring that those dependencies will be
resolved correctly, and will also be updated whenever a dependent element is
updated.

- Note that the equality matching rule for the attributeTypes, objectClasses,
nameForms, matchingRules, matchingRuleUse, ditContentRules, and
ditStructureRules attribute types has been changed to the caseIgnoreMatch
rule. This does deviate from the standard somewhat, but allows the server to
provide notably better performance while still exhibiting correct behavior as
far as clients are concerned.


OpenDS Issue Number: 366
2 files added
25 files modified
10621 ■■■■ changed files
opendj-sdk/opends/resource/schema/00-core.ldif 28 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java 3003 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java 42 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java 5 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java 800 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java 17 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/plugins/EntryUUIDPlugin.java 14 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java 14 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/DITContentRuleSyntax.java 41 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/DITStructureRuleSyntax.java 78 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/MatchingRuleUseSyntax.java 29 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/NameFormSyntax.java 34 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java 9 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java 87 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/CommonSchemaElements.java 99 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java 710 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java 485 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java 452 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java 553 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java 133 ●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/Schema.java 264 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java 107 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java 51 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java 3041 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaTestMatchingRule.java 190 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java 167 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java 168 ●●●●● patch | view | raw | blame | history
opendj-sdk/opends/resource/schema/00-core.ldif
@@ -21,7 +21,7 @@
# CDDL HEADER END
#
#
#      Portions Copyright 2006 Sun Microsystems, Inc.
#      Portions Copyright 2006-2007 Sun Microsystems, Inc.
#
#
# This file contains a core set of attribute type and objectlass definitions
@@ -172,20 +172,16 @@
  EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12
  SINGLE-VALUE NO-USER-MODIFICATION USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.5 NAME 'attributeTypes'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.5 NAME 'attributeTypes' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.3 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.6 NAME 'objectClasses'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.6 NAME 'objectClasses' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.37 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.4 NAME 'matchingRules'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.4 NAME 'matchingRules' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.30 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.8 NAME 'matchingRuleUse'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.8 NAME 'matchingRuleUse' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.31 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 1.3.6.1.4.1.1466.101.120.5 NAME 'namingContexts'
@@ -206,15 +202,13 @@
attributeTypes: ( 1.3.6.1.4.1.1466.101.120.16 NAME 'ldapSyntaxes'
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.54 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.1 NAME 'dITStructureRules'
  EQUALITY integerFirstComponentMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.17
  USAGE directoryOperation X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.7 NAME 'nameForms'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.1 NAME 'dITStructureRules' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.17 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.7 NAME 'nameForms' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.35 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 2.5.21.2 NAME 'dITContentRules'
  EQUALITY objectIdentifierFirstComponentMatch
attributeTypes: ( 2.5.21.2 NAME 'dITContentRules' EQUALITY caseIgnoreMatch
  SYNTAX 1.3.6.1.4.1.1466.115.121.1.16 USAGE directoryOperation
  X-ORIGIN 'RFC 2252' )
attributeTypes: ( 0.9.2342.19200300.100.1.25 NAME 'dc'
@@ -464,7 +458,7 @@
  AUXILIARY X-ORIGIN 'RFC 2252' )
objectClasses: ( 2.5.20.1 NAME 'subschema' AUXILIARY MAY ( dITStructureRules $
  nameForms $ ditContentRules $ objectClasses $ attributeTypes $ matchingRules $
  matchingRuleUse ) X-ORIGIn 'RFC 2252' )
  matchingRuleUse ) X-ORIGIN 'RFC 2252' )
objectClasses: ( 0.9.2342.19200300.100.4.5 NAME 'account' SUP top STRUCTURAL
  MUST uid MAY ( description $ seeAlso $ l $ o $ ou $ host )
  X-ORIGIN 'RFC 4524' )
opendj-sdk/opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.backends;
@@ -32,6 +32,7 @@
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
@@ -43,6 +44,8 @@
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.Deflater;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
@@ -52,8 +55,10 @@
import javax.crypto.CipherOutputStream;
import javax.crypto.Mac;
import org.opends.server.api.AlertGenerator;
import org.opends.server.api.Backend;
import org.opends.server.api.ConfigurableComponent;
import org.opends.server.api.MatchingRule;
import org.opends.server.config.BooleanConfigAttribute;
import org.opends.server.config.ConfigAttribute;
import org.opends.server.config.ConfigEntry;
@@ -67,6 +72,10 @@
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.core.SearchOperation;
import org.opends.server.schema.AttributeTypeSyntax;
import org.opends.server.schema.DITContentRuleSyntax;
import org.opends.server.schema.DITStructureRuleSyntax;
import org.opends.server.schema.MatchingRuleUseSyntax;
import org.opends.server.schema.NameFormSyntax;
import org.opends.server.schema.ObjectClassSyntax;
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
@@ -77,6 +86,8 @@
import org.opends.server.types.ConfigChangeResult;
import org.opends.server.types.CryptoManager;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DITContentRule;
import org.opends.server.types.DITStructureRule;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ErrorLogCategory;
@@ -85,16 +96,21 @@
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.MatchingRuleUse;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.NameForm;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.ObjectClassType;
import org.opends.server.types.RDN;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.Schema;
import org.opends.server.types.SchemaFileElement;
import org.opends.server.types.SearchFilter;
import org.opends.server.types.SearchScope;
import org.opends.server.util.DynamicConstants;
import org.opends.server.util.LDIFReader;
import org.opends.server.util.LDIFException;
import org.opends.server.util.LDIFWriter;
import static org.opends.server.config.ConfigConstants.*;
@@ -114,7 +130,7 @@
 */
public class SchemaBackend
       extends Backend
       implements ConfigurableComponent
       implements ConfigurableComponent, AlertGenerator
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -844,7 +860,8 @@
    // elements, nor will we allow modification of any other attributes.  Make
    // sure that the included modify operation is acceptable within these
    // constraints.
    List<Modification> mods = modifyOperation.getModifications();
    ArrayList<Modification> mods =
         new ArrayList<Modification>(modifyOperation.getModifications());
    if (mods.isEmpty())
    {
      // There aren't any modifications, so we don't need to do anything.
@@ -852,11 +869,15 @@
    }
    Schema newSchema = DirectoryServer.getSchema().duplicate();
    LinkedList<AttributeType> newAttrTypes = new LinkedList<AttributeType>();
    LinkedList<ObjectClass> newObjectClasses = new LinkedList<ObjectClass>();
    TreeSet<String> modifiedSchemaFiles = new TreeSet<String>();
    LinkedHashSet<SchemaFileElement> dependentElements =
         new LinkedHashSet<SchemaFileElement>();
    int pos = -1;
    for (Modification m : mods)
    {
      pos++;
      if (m.isInternal())
      {
        // We don't need to do anything for internal modifications (e.g., like
@@ -864,301 +885,2026 @@
        continue;
      }
      // Determine the type of modification to perform.  We will support add and
      // delete operations in the schema, and we will also support the ability
      // to add a schema element that already exists and treat it as a
      // replacement of that existing element.
      Attribute     a  = m.getAttribute();
      AttributeType at = a.getAttributeType();
      switch (m.getModificationType())
      {
        case ADD:
          // This is fine, as long as there aren't any conflicts later.
          LinkedHashSet<AttributeValue> values = a.getValues();
          if (values.isEmpty())
          {
            continue;
          }
          if (at.equals(attributeTypesType))
          {
            for (AttributeValue v : values)
            {
              AttributeType type;
              try
              {
                type = AttributeTypeSyntax.decodeAttributeType(v.getValue(),
                                                               newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addAttributeType(type, newSchema, modifiedSchemaFiles);
            }
          }
          else if (at.equals(objectClassesType))
          {
            for (AttributeValue v : values)
            {
              ObjectClass oc;
              try
              {
                oc = ObjectClassSyntax.decodeObjectClass(v.getValue(),
                                                         newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addObjectClass(oc, newSchema, modifiedSchemaFiles);
            }
          }
          else if (at.equals(nameFormsType))
          {
            for (AttributeValue v : values)
            {
              NameForm nf;
              try
              {
                nf = NameFormSyntax.decodeNameForm(v.getValue(), newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addNameForm(nf, newSchema, modifiedSchemaFiles);
            }
          }
          else if (at.equals(ditContentRulesType))
          {
            for (AttributeValue v : values)
            {
              DITContentRule dcr;
              try
              {
                dcr = DITContentRuleSyntax.decodeDITContentRule(v.getValue(),
                                                                newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DCR;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addDITContentRule(dcr, newSchema, modifiedSchemaFiles);
            }
          }
          else if (at.equals(ditStructureRulesType))
          {
            for (AttributeValue v : values)
            {
              DITStructureRule dsr;
              try
              {
                dsr = DITStructureRuleSyntax.decodeDITStructureRule(
                           v.getValue(), newSchema, false);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DSR;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addDITStructureRule(dsr, newSchema, modifiedSchemaFiles);
            }
          }
          else if (at.equals(matchingRuleUsesType))
          {
            for (AttributeValue v : values)
            {
              MatchingRuleUse mru;
              try
              {
                mru = MatchingRuleUseSyntax.decodeMatchingRuleUse(v.getValue(),
                                                                  newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_MR_USE;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              addMatchingRuleUse(mru, newSchema, modifiedSchemaFiles);
            }
          }
          else
          {
            int    msgID   = MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE;
            String message = getMessage(msgID, a.getName());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
          break;
        case DELETE:
          // FIXME -- We need to support this.
          int    msgID   = MSGID_SCHEMA_DELETE_MODTYPE_NOT_SUPPORTED;
          String message = getMessage(msgID);
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                       msgID);
          values = a.getValues();
          if (values.isEmpty())
          {
            int    msgID   = MSGID_SCHEMA_MODIFY_DELETE_NO_VALUES;
            String message = getMessage(msgID, a.getName());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
        case REPLACE:
          // FIXME -- Should we support this?
        case INCREMENT:
          if (at.equals(attributeTypesType))
          {
            for (AttributeValue v : values)
            {
              AttributeType type;
              try
              {
                type = AttributeTypeSyntax.decodeAttributeType(v.getValue(),
                                                               newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeAttributeType(type, newSchema, mods, pos,
                                  modifiedSchemaFiles);
            }
          }
          else if (at.equals(objectClassesType))
          {
            for (AttributeValue v : values)
            {
              ObjectClass oc;
              try
              {
                oc = ObjectClassSyntax.decodeObjectClass(v.getValue(),
                                                         newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeObjectClass(oc, newSchema, mods, pos, modifiedSchemaFiles);
            }
          }
          else if (at.equals(nameFormsType))
          {
            for (AttributeValue v : values)
            {
              NameForm nf;
              try
              {
                nf = NameFormSyntax.decodeNameForm(v.getValue(), newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeNameForm(nf, newSchema, mods, pos, modifiedSchemaFiles);
            }
          }
          else if (at.equals(ditContentRulesType))
          {
            for (AttributeValue v : values)
            {
              DITContentRule dcr;
              try
              {
                dcr = DITContentRuleSyntax.decodeDITContentRule(v.getValue(),
                                                                newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DCR;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeDITContentRule(dcr, newSchema, mods, pos,
                                   modifiedSchemaFiles);
            }
          }
          else if (at.equals(ditStructureRulesType))
          {
            for (AttributeValue v : values)
            {
              DITStructureRule dsr;
              try
              {
                dsr = DITStructureRuleSyntax.decodeDITStructureRule(
                           v.getValue(), newSchema, false);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DSR;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeDITStructureRule(dsr, newSchema, mods, pos,
                                     modifiedSchemaFiles);
            }
          }
          else if (at.equals(matchingRuleUsesType))
          {
            for (AttributeValue v : values)
            {
              MatchingRuleUse mru;
              try
              {
                mru = MatchingRuleUseSyntax.decodeMatchingRuleUse(v.getValue(),
                                                                  newSchema);
              }
              catch (DirectoryException de)
              {
                assert debugException(CLASS_NAME, "replaceEntry", de);
                int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_MR_USE;
                String message = getMessage(msgID, v.getStringValue(),
                                            de.getErrorMessage());
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID, de);
              }
              removeMatchingRuleUse(mru, newSchema, mods, pos,
                                    modifiedSchemaFiles);
            }
          }
          else
          {
            int    msgID   = MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE;
            String message = getMessage(msgID, a.getName());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
          break;
        default:
          // FIXME -- Make sure to update this message once we support
          // schema deletes and possibly replace.
          msgID   = MSGID_SCHEMA_INVALID_MODIFICATION_TYPE;
          message = getMessage(msgID, m.getModificationType());
          int    msgID   = MSGID_SCHEMA_INVALID_MODIFICATION_TYPE;
          String message = getMessage(msgID, m.getModificationType());
          throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                       msgID);
      }
      // At the present time, we will only allow modification of the
      // attributeTypes and objectClasses attributes.
      Attribute     a = m.getAttribute();
      AttributeType t = a.getAttributeType();
      if (t.equals(attributeTypesType))
      {
        for (AttributeValue v : a.getValues())
        {
          AttributeType newType;
          try
          {
            newType = AttributeTypeSyntax.decodeAttributeType(v.getValue(),
                                                              newSchema);
          }
          catch (DirectoryException de)
          {
            assert debugException(CLASS_NAME, "replaceEntry", de);
            int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE;
            String message = getMessage(msgID, v.getStringValue(),
                                        de.getErrorMessage());
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                         message, msgID, de);
          }
          try
          {
            newSchema.registerAttributeType(newType, false);
            newAttrTypes.add(newType);
          }
          catch (DirectoryException de)
          {
            assert debugException(CLASS_NAME, "replaceEntry", de);
            int    msgID   = MSGID_SCHEMA_MODIFY_ATTRTYPE_ALREADY_EXISTS;
            String message = getMessage(msgID, newType.getNameOrOID(),
                                        de.getErrorMessage());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID, de);
          }
        }
      }
      else if (t.equals(objectClassesType))
      {
        for (AttributeValue v : a.getValues())
        {
          ObjectClass newClass;
          try
          {
            newClass = ObjectClassSyntax.decodeObjectClass(v.getValue(),
                                                           newSchema);
          }
          catch (DirectoryException de)
          {
            assert debugException(CLASS_NAME, "replaceEntry", de);
            int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS;
            String message = getMessage(msgID, v.getStringValue(),
                                        de.getErrorMessage());
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                         message, msgID, de);
          }
          // If there is a superior class, then make sure it is defined.
          ObjectClass superiorClass = newClass.getSuperiorClass();
          if (superiorClass != null)
          {
            String lowerName = toLowerCase(superiorClass.getNameOrOID());
            if (! newSchema.hasObjectClass(lowerName))
            {
              int msgID = MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS;
              String message = getMessage(msgID, newClass.getNameOrOID(),
                                          superiorClass.getNameOrOID());
              throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                           message, msgID);
            }
          }
          // Make sure that all the associated attribute types are defined.
          for (AttributeType at : newClass.getRequiredAttributes())
          {
            String lowerName = toLowerCase(at.getNameOrOID());
            if (! newSchema.hasAttributeType(lowerName))
            {
              int    msgID   = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_REQUIRED_ATTR;
              String message = getMessage(msgID, newClass.getNameOrOID(),
                                          at.getNameOrOID());
              throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                           message, msgID);
            }
          }
          for (AttributeType at : newClass.getOptionalAttributes())
          {
            String lowerName = toLowerCase(at.getNameOrOID());
            if (! newSchema.hasAttributeType(lowerName))
            {
              int    msgID   = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_OPTIONAL_ATTR;
              String message = getMessage(msgID, newClass.getNameOrOID(),
                                          at.getNameOrOID());
              throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                           message, msgID);
            }
          }
          try
          {
            newSchema.registerObjectClass(newClass, false);
            newObjectClasses.add(newClass);
          }
          catch (DirectoryException de)
          {
            assert debugException(CLASS_NAME, "replaceEntry", de);
            int    msgID   = MSGID_SCHEMA_MODIFY_OBJECTCLASS_ALREADY_EXISTS;
            String message = getMessage(msgID, newClass.getNameOrOID(),
                                        de.getErrorMessage());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID, de);
          }
        }
      }
      else
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE;
        String message = getMessage(msgID, a.getName());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If we've gotten here, then everything looks OK.  Add the new schema
    // elements to the 99-user.ldif file and swing the new schema into place.
    String schemaDirPath = SchemaConfigManager.getSchemaDirectoryPath();
    File userSchemaFile = new File(schemaDirPath, FILE_USER_SCHEMA_ELEMENTS);
    Entry userSchemaEntry = null;
    if (userSchemaFile.exists())
    {
      // There's already a set of user-defined schema elements, so we'll need to
      // add these new elements to that set.
      LDIFReader ldifReader = null;
      try
      {
        LDIFImportConfig importConfig =
             new LDIFImportConfig(userSchemaFile.getAbsolutePath());
        ldifReader = new LDIFReader(importConfig);
        userSchemaEntry = ldifReader.readEntry(true);
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "replaceEntry", e);
        int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_READ_EXISTING_USER_SCHEMA;
        String message = getMessage(msgID, userSchemaFile.getAbsolutePath(),
                                    stackTraceToSingleLineString(e));
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, e);
      }
      finally
      {
        if (ldifReader != null)
        {
          ldifReader.close();
        }
      }
    }
    if (userSchemaEntry == null)
    {
      // This could happen if there was no user schema file or if it was there
      // but didn't have any entries.  At any rate, create a new, empty entry.
      userSchemaEntry = createEmptySchemaEntry();
    }
    // Add all of the new schema elements to the entry.
    if (! newAttrTypes.isEmpty())
    {
      LinkedHashSet<AttributeValue> values =
           new LinkedHashSet<AttributeValue>();
      for (AttributeType t : newAttrTypes)
      {
        StringBuilder buffer = new StringBuilder();
        t.toString(buffer, false);
        values.add(new AttributeValue(attributeTypesType, buffer.toString()));
      }
      Attribute attrTypeAttribute = new Attribute(attributeTypesType,
                                                  ATTR_ATTRIBUTE_TYPES, values);
      LinkedList<AttributeValue> duplicateValues =
           new LinkedList<AttributeValue>();
      userSchemaEntry.addAttribute(attrTypeAttribute, duplicateValues);
    }
    if (! newObjectClasses.isEmpty())
    {
      LinkedHashSet<AttributeValue> values =
           new LinkedHashSet<AttributeValue>();
      for (ObjectClass oc : newObjectClasses)
      {
        StringBuilder buffer = new StringBuilder();
        oc.toString(buffer, false);
        values.add(new AttributeValue(attributeTypesType, buffer.toString()));
      }
      Attribute ocAttribute = new Attribute(objectClassesType,
                                            ATTR_OBJECTCLASSES, values);
      LinkedList<AttributeValue> duplicateValues =
           new LinkedList<AttributeValue>();
      userSchemaEntry.addAttribute(ocAttribute, duplicateValues);
    }
    // Swing the new schema into place.
    // If we've gotten here, then everything looks OK.  We'll re-write all
    // impacted schema files by first creating them in a temporary location
    // and then replacing the existing schema files with the new versions.
    // If all that goes successfully, then activate the new schema.
    HashMap<String,File> tempSchemaFiles = new HashMap<String,File>();
    try
    {
      File tempSchemaFile = new File(userSchemaFile.getAbsolutePath() + ".tmp");
      LDIFExportConfig exportConfig =
           new LDIFExportConfig(tempSchemaFile.getAbsolutePath(),
                                ExistingFileBehavior.OVERWRITE);
      LDIFWriter writer = null;
      try
      for (String schemaFile : modifiedSchemaFiles)
      {
        writer = new LDIFWriter(exportConfig);
        writer.writeEntry(userSchemaEntry);
      }
      finally
      {
        if (writer != null)
        {
          writer.close();
        }
        File tempSchemaFile = writeTempSchemaFile(newSchema, schemaFile);
        tempSchemaFiles.put(schemaFile, tempSchemaFile);
      }
      File oldSchemaFile = null;
      if (userSchemaFile.exists())
      {
        oldSchemaFile = new File(userSchemaFile.getAbsolutePath() + ".old");
        if (oldSchemaFile.exists())
        {
          oldSchemaFile.delete();
        }
        userSchemaFile.renameTo(oldSchemaFile);
      }
      tempSchemaFile.renameTo(userSchemaFile);
      if (oldSchemaFile != null)
      {
        oldSchemaFile.delete();
      }
      installSchemaFiles(tempSchemaFiles);
      DirectoryServer.setSchema(newSchema);
    }
    catch (DirectoryException de)
    {
      assert debugException(CLASS_NAME, "replaceEntry", de);
      throw de;
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "replaceEntry", e);
      int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA;
      String message = getMessage(msgID, userSchemaFile.getAbsolutePath(),
                                  stackTraceToSingleLineString(e));
      String message = getMessage(msgID, stackTraceToSingleLineString(e));
      throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                   message, msgID, e);
    }
    finally
    {
      cleanUpTempSchemaFiles(tempSchemaFiles);
    }
  }
  /**
   * Handles all processing required for adding the provided attribute type to
   * the given schema, replacing an existing type if necessary, and ensuring all
   * other metadata is properly updated.
   *
   * @param  attributeType        The attribute type to add or replace in the
   *                              server schema.
   * @param  schema               The schema to which the attribute type should
   *                              be added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided attribute type to the server
   *                              schema.
   */
  private void addAttributeType(AttributeType attributeType, Schema schema,
                                Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addAttributeType",
                      String.valueOf(attributeType), String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified attribute type already exists.  We'll check
    // the OID and all of the names, which means that it's possible there could
    // be more than one match (although if there is, then we'll refuse the
    // operation).
    AttributeType existingType =
         schema.getAttributeType(attributeType.getOID());
    for (String name : attributeType.getNormalizedNames())
    {
      AttributeType t = schema.getAttributeType(name);
      if (t == null)
      {
        continue;
      }
      else if (existingType == null)
      {
        existingType = t;
      }
      else if (existingType != t)
      {
        // NOTE:  We really do want to use "!=" instead of "! t.equals()"
        // because we want to check whether it's the same object instance, not
        // just a logical equivalent.
        int msgID = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_ATTRTYPE;
        String message = getMessage(msgID, attributeType.getNameOrOID(),
                                    existingType.getNameOrOID(),
                                    t.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the new attribute type doesn't reference an undefined
    // superior attribute type.
    AttributeType superiorType = attributeType.getSuperiorType();
    if (superiorType != null)
    {
      if (! schema.hasAttributeType(superiorType.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_ATTRIBUTE_TYPE;
        String message = getMessage(msgID, attributeType.getNameOrOID(),
                                    superiorType.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If there is no existing type, then we're adding a new attribute.
    // Otherwise, we're replacing an existing one.
    if (existingType == null)
    {
      schema.registerAttributeType(attributeType, false);
      String schemaFile = attributeType.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        attributeType.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterAttributeType(existingType);
      schema.registerAttributeType(attributeType, false);
      schema.rebuildDependentElements(existingType);
      if ((attributeType.getSchemaFile() == null) ||
          (attributeType.getSchemaFile().length() == 0))
      {
        String schemaFile = existingType.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        attributeType.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = attributeType.getSchemaFile();
        String oldSchemaFile = existingType.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided attribute type from
   * the server schema, ensuring all other metadata is properly updated.  Note
   * that this method will first check to see whether the same attribute type
   * will be later added to the server schema with an updated definition, and if
   * so then the removal will be ignored because the later add will be handled
   * as a replace.  If the attribute type will not be replaced with a new
   * definition, then this method will ensure that there are no other schema
   * elements that depend on the attribute type before allowing it to be
   * removed.
   *
   * @param  attributeType        The attribute type to remove from the server
   *                              schema.
   * @param  schema               The schema from which the attribute type
   *                              should be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided attribute type from the server
   *                              schema.
   */
  private void removeAttributeType(AttributeType attributeType, Schema schema,
                                   ArrayList<Modification> modifications,
                                   int currentPosition,
                                   Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeAttributeType",
                      String.valueOf(attributeType), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified attribute type is actually defined in the server
    // schema.  If not, then fail.
    AttributeType removeType = schema.getAttributeType(attributeType.getOID());
    if ((removeType == null) || (! removeType.equals(attributeType)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_ATTRIBUTE_TYPE;
      String message = getMessage(msgID, attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // See if there is another modification later to add the attribute type back
    // into the schema.  If so, then it's a replace and we should ignore the
    // remove because adding it back will handle the replace.
    for (int i=currentPosition+1; i < modifications.size(); i++)
    {
      Modification m = modifications.get(i);
      Attribute    a = m.getAttribute();
      if ((m.getModificationType() != ModificationType.ADD) ||
          (! a.getAttributeType().equals(attributeTypesType)))
      {
        continue;
      }
      for (AttributeValue v : a.getValues())
      {
        AttributeType at;
        try
        {
          at = AttributeTypeSyntax.decodeAttributeType(v.getValue(), schema);
        }
        catch (DirectoryException de)
        {
          assert debugException(CLASS_NAME, "removeAttributeType", de);
          int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_ATTRTYPE;
          String message = getMessage(msgID, v.getStringValue(),
                                      de.getErrorMessage());
          throw new DirectoryException(
                         ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                         msgID, de);
        }
        if (attributeType.getOID().equals(at.getOID()))
        {
          // We found a match where the attribute type is added back later, so
          // we don't need to do anything else here.
          return;
        }
      }
    }
    // Make sure that the attribute type isn't used as the superior type for
    // any other attributes.
    for (AttributeType at : schema.getAttributeTypes().values())
    {
      AttributeType superiorType = at.getSuperiorType();
      if ((superiorType != null) && superiorType.equals(removeType))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_AT_SUPERIOR_TYPE;
        String message = getMessage(msgID, removeType.getNameOrOID(),
                                    superiorType.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the attribute type isn't used as a required or optional
    // attribute type in any objectclass.
    for (ObjectClass oc : schema.getObjectClasses().values())
    {
      if (oc.getRequiredAttributes().contains(removeType) ||
          oc.getOptionalAttributes().contains(removeType))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_OC;
        String message = getMessage(msgID, removeType.getNameOrOID(),
                                    oc.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the attribute type isn't used as a required or optional
    // attribute type in any name form.
    for (NameForm nf : schema.getNameFormsByObjectClass().values())
    {
      if (nf.getRequiredAttributes().contains(removeType) ||
          nf.getOptionalAttributes().contains(removeType))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_NF;
        String message = getMessage(msgID, removeType.getNameOrOID(),
                                    nf.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the attribute type isn't used as a required, optional, or
    // prohibited attribute type in any DIT content rule.
    for (DITContentRule dcr : schema.getDITContentRules().values())
    {
      if (dcr.getRequiredAttributes().contains(removeType) ||
          dcr.getOptionalAttributes().contains(removeType) ||
          dcr.getProhibitedAttributes().contains(removeType))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_DCR;
        String message = getMessage(msgID, removeType.getNameOrOID(),
                                    dcr.getName());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the attribute type isn't referenced by any matching rule
    // use.
    for (MatchingRuleUse mru : schema.getMatchingRuleUses().values())
    {
      if (mru.getAttributes().contains(removeType))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_MR_USE;
        String message = getMessage(msgID, removeType.getNameOrOID(),
                                    mru.getName());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If we've gotten here, then it's OK to remove the attribute type from
    // the schema.
    schema.deregisterAttributeType(removeType);
    String schemaFile = removeType.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
  /**
   * Handles all processing required for adding the provided objectclass to the
   * given schema, replacing an existing class if necessary, and ensuring
   * all other metadata is properly updated.
   *
   * @param  objectClass          The objectclass to add or replace in the
   *                              server schema.
   * @param  schema               The schema to which the objectclass should be
   *                              added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided objectclass to the server schema.
   */
  private void addObjectClass(ObjectClass objectClass, Schema schema,
                              Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addObjectClass", String.valueOf(objectClass),
                      String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified objectclass already exists.  We'll check the
    // OID and all of the names, which means that it's possible there could be
    // more than one match (although if there is, then we'll refuse the
    // operation).
    ObjectClass existingClass =
         schema.getObjectClass(objectClass.getOID());
    for (String name : objectClass.getNormalizedNames())
    {
      ObjectClass oc = schema.getObjectClass(name);
      if (oc == null)
      {
        continue;
      }
      else if (existingClass == null)
      {
        existingClass = oc;
      }
      else if (existingClass != oc)
      {
        // NOTE:  We really do want to use "!=" instead of "! t.equals()"
        // because we want to check whether it's the same object instance, not
        // just a logical equivalent.
        int msgID = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_OBJECTCLASS;
        String message = getMessage(msgID, objectClass.getNameOrOID(),
                                    existingClass.getNameOrOID(),
                                    oc.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the new objectclass doesn't reference an undefined
    // superior class, or an undefined required or optional attribute type.
    ObjectClass superiorClass = objectClass.getSuperiorClass();
    if (superiorClass != null)
    {
      if (! schema.hasObjectClass(superiorClass.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS;
        String message = getMessage(msgID, objectClass.getNameOrOID(),
                                    superiorClass.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : objectClass.getRequiredAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_REQUIRED_ATTR;
        String message = getMessage(msgID, objectClass.getNameOrOID(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : objectClass.getOptionalAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_OC_UNDEFINED_OPTIONAL_ATTR;
        String message = getMessage(msgID, objectClass.getNameOrOID(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If there is no existing class, then we're adding a new objectclass.
    // Otherwise, we're replacing an existing one.
    if (existingClass == null)
    {
      schema.registerObjectClass(objectClass, false);
      String schemaFile = objectClass.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        objectClass.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterObjectClass(existingClass);
      schema.registerObjectClass(objectClass, false);
      schema.rebuildDependentElements(existingClass);
      if ((objectClass.getSchemaFile() == null) ||
          (objectClass.getSchemaFile().length() == 0))
      {
        String schemaFile = existingClass.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        objectClass.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = objectClass.getSchemaFile();
        String oldSchemaFile = existingClass.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided objectclass from the
   * server schema, ensuring all other metadata is properly updated.  Note that
   * this method will first check to see whether the same objectclass will be
   * later added to the server schema with an updated definition, and if so then
   * the removal will be ignored because the later add will be handled as a
   * replace.  If the objectclass will not be replaced with a new definition,
   * then this method will ensure that there are no other schema elements that
   * depend on the objectclass before allowing it to be removed.
   *
   * @param  objectClass          The objectclass to remove from the server
   *                              schema.
   * @param  schema               The schema from which the objectclass should
   *                              be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided objectclass from the server
   *                              schema.
   */
  private void removeObjectClass(ObjectClass objectClass, Schema schema,
                                 ArrayList<Modification> modifications,
                                 int currentPosition,
                                 Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeObjectClass",
                      String.valueOf(objectClass), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified objectclass is actually defined in the server
    // schema.  If not, then fail.
    ObjectClass removeClass = schema.getObjectClass(objectClass.getOID());
    if ((removeClass == null) || (! removeClass.equals(objectClass)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_OBJECTCLASS;
      String message = getMessage(msgID, objectClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // See if there is another modification later to add the objectclass back
    // into the schema.  If so, then it's a replace and we should ignore the
    // remove because adding it back will handle the replace.
    for (int i=currentPosition+1; i < modifications.size(); i++)
    {
      Modification m = modifications.get(i);
      Attribute    a = m.getAttribute();
      if ((m.getModificationType() != ModificationType.ADD) ||
          (! a.getAttributeType().equals(objectClassesType)))
      {
        continue;
      }
      for (AttributeValue v : a.getValues())
      {
        ObjectClass oc;
        try
        {
          oc = ObjectClassSyntax.decodeObjectClass(v.getValue(), schema);
        }
        catch (DirectoryException de)
        {
          assert debugException(CLASS_NAME, "removeObjectClass", de);
          int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS;
          String message = getMessage(msgID, v.getStringValue(),
                                      de.getErrorMessage());
          throw new DirectoryException(
                         ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                         msgID, de);
        }
        if (objectClass.getOID().equals(oc.getOID()))
        {
          // We found a match where the objectClass is added back later, so we
          // don't need to do anything else here.
          return;
        }
      }
    }
    // Make sure that the objectclass isn't used as the superior class for any
    // other objectclass.
    for (ObjectClass oc : schema.getObjectClasses().values())
    {
      ObjectClass superiorClass = oc.getSuperiorClass();
      if ((superiorClass != null) && superiorClass.equals(removeClass))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_OC_SUPERIOR_CLASS;
        String message = getMessage(msgID, removeClass.getNameOrOID(),
                                    superiorClass.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the objectclass isn't used as the structural class for
    // any name form.
    NameForm nf = schema.getNameForm(removeClass);
    if (nf != null)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_NF;
      String message = getMessage(msgID, removeClass.getNameOrOID(),
                                  nf.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Make sure that the objectclass isn't used as a structural or auxiliary
    // class for any DIT content rule.
    for (DITContentRule dcr : schema.getDITContentRules().values())
    {
      if (dcr.getStructuralClass().equals(removeClass) ||
          dcr.getAuxiliaryClasses().contains(removeClass))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_DCR;
        String message = getMessage(msgID, removeClass.getNameOrOID(),
                                    dcr.getName());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If we've gotten here, then it's OK to remove the objectclass from the
    // schema.
    schema.deregisterObjectClass(removeClass);
    String schemaFile = removeClass.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
  /**
   * Handles all processing required for adding the provided name form to the
   * the given schema, replacing an existing name form if necessary, and
   * ensuring all other metadata is properly updated.
   *
   * @param  nameForm             The name form to add or replace in the server
   *                              schema.
   * @param  schema               The schema to which the name form should be
   *                              added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided name form to the server schema.
   */
  private void addNameForm(NameForm nameForm, Schema schema,
                           Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addNameForm", String.valueOf(nameForm),
                      String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified name form already exists.  We'll check the
    // OID and all of the names, which means that it's possible there could be
    // more than one match (although if there is, then we'll refuse the
    // operation).
    NameForm existingNF =
         schema.getNameForm(nameForm.getOID());
    for (String name : nameForm.getNames().keySet())
    {
      NameForm nf = schema.getNameForm(name);
      if (nf == null)
      {
        continue;
      }
      else if (existingNF == null)
      {
        existingNF = nf;
      }
      else if (existingNF != nf)
      {
        // NOTE:  We really do want to use "!=" instead of "! t.equals()"
        // because we want to check whether it's the same object instance, not
        // just a logical equivalent.
        int msgID = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_NAME_FORM;
        String message = getMessage(msgID, nameForm.getNameOrOID(),
                                    existingNF.getNameOrOID(),
                                    nf.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // Make sure that the new name form doesn't reference an undefined
    // structural class, or an undefined required or optional attribute type.
    ObjectClass structuralClass = nameForm.getStructuralClass();
    if (! schema.hasObjectClass(structuralClass.getOID()))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_NF_UNDEFINED_STRUCTURAL_OC;
      String message = getMessage(msgID, nameForm.getNameOrOID(),
                                  structuralClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_NF_OC_NOT_STRUCTURAL;
      String message = getMessage(msgID, nameForm.getNameOrOID(),
                                  structuralClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    NameForm existingNFForClass = schema.getNameForm(structuralClass);
    if ((existingNFForClass != null) && (existingNFForClass != existingNF))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_NF;
      String message = getMessage(msgID, nameForm.getNameOrOID(),
                                  structuralClass.getNameOrOID(),
                                  existingNFForClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    for (AttributeType at : nameForm.getRequiredAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_NF_UNDEFINED_REQUIRED_ATTR;
        String message = getMessage(msgID, nameForm.getNameOrOID(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : nameForm.getOptionalAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_NF_UNDEFINED_OPTIONAL_ATTR;
        String message = getMessage(msgID, nameForm.getNameOrOID(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If there is no existing class, then we're adding a new name form.
    // Otherwise, we're replacing an existing one.
    if (existingNF == null)
    {
      schema.registerNameForm(nameForm, false);
      String schemaFile = nameForm.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        nameForm.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterNameForm(existingNF);
      schema.registerNameForm(nameForm, false);
      schema.rebuildDependentElements(existingNF);
      if ((nameForm.getSchemaFile() == null) ||
          (nameForm.getSchemaFile().length() == 0))
      {
        String schemaFile = existingNF.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        nameForm.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = nameForm.getSchemaFile();
        String oldSchemaFile = existingNF.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided name form from the
   * server schema, ensuring all other metadata is properly updated.  Note that
   * this method will first check to see whether the same name form will be
   * later added to the server schema with an updated definition, and if so then
   * the removal will be ignored because the later add will be handled as a
   * replace.  If the name form will not be replaced with a new definition, then
   * this method will ensure that there are no other schema elements that depend
   * on the name form before allowing it to be removed.
   *
   * @param  nameForm             The name form to remove from the server
   *                              schema.
   * @param  schema               The schema from which the name form should be
   *                              be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided name form from the server schema.
   */
  private void removeNameForm(NameForm nameForm, Schema schema,
                              ArrayList<Modification> modifications,
                              int currentPosition,
                              Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeNameForm",
                      String.valueOf(nameForm), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified name form is actually defined in the server schema.
    // If not, then fail.
    NameForm removeNF = schema.getNameForm(nameForm.getOID());
    if ((removeNF == null) || (! removeNF.equals(nameForm)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_NAME_FORM;
      String message = getMessage(msgID, nameForm.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // See if there is another modification later to add the name form back
    // into the schema.  If so, then it's a replace and we should ignore the
    // remove because adding it back will handle the replace.
    for (int i=currentPosition+1; i < modifications.size(); i++)
    {
      Modification m = modifications.get(i);
      Attribute    a = m.getAttribute();
      if ((m.getModificationType() != ModificationType.ADD) ||
          (! a.getAttributeType().equals(nameFormsType)))
      {
        continue;
      }
      for (AttributeValue v : a.getValues())
      {
        NameForm nf;
        try
        {
          nf = NameFormSyntax.decodeNameForm(v.getValue(), schema);
        }
        catch (DirectoryException de)
        {
          assert debugException(CLASS_NAME, "removeNameForm", de);
          int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM;
          String message = getMessage(msgID, v.getStringValue(),
                                      de.getErrorMessage());
          throw new DirectoryException(
                         ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                         msgID, de);
        }
        if (nameForm.getOID().equals(nf.getOID()))
        {
          // We found a match where the name form is added back later, so we
          // don't need to do anything else here.
          return;
        }
      }
    }
    // Make sure that the name form isn't referenced by any DIT structure
    // rule.
    DITStructureRule dsr = schema.getDITStructureRule(removeNF);
    if (dsr != null)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NF_IN_DSR;
      String message = getMessage(msgID, removeNF.getNameOrOID(),
                                  dsr.getNameOrRuleID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // If we've gotten here, then it's OK to remove the name form from the
    // schema.
    schema.deregisterNameForm(removeNF);
    String schemaFile = removeNF.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
  /**
   * Handles all processing required for adding the provided DIT content rule to
   * the given schema, replacing an existing rule if necessary, and ensuring
   * all other metadata is properly updated.
   *
   * @param  ditContentRule       The DIT content rule to add or replace in the
   *                              server schema.
   * @param  schema               The schema to which the DIT content rule
   *                              should be be added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided DIT content rule to the server
   *                              schema.
   */
  private void addDITContentRule(DITContentRule ditContentRule, Schema schema,
                                 Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addDITContentRule",
                      String.valueOf(ditContentRule), String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified DIT content rule already exists.  We'll check
    // all of the names, which means that it's possible there could be more than
    // one match (although if there is, then we'll refuse the operation).
    DITContentRule existingDCR = null;
    for (DITContentRule dcr : schema.getDITContentRules().values())
    {
      for (String name : ditContentRule.getNames().keySet())
      {
        if (dcr.hasName(name))
        {
          if (existingDCR == null)
          {
            existingDCR = dcr;
            break;
          }
          else
          {
            int    msgID   = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DCR;
            String message = getMessage(msgID, ditContentRule.getName(),
                                        existingDCR.getName(),
                                        dcr.getName());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
        }
      }
    }
    // Get the structural class for the new DIT content rule and see if there's
    // already an existing rule that is associated with that class.  If there
    // is, then it will only be acceptable if it's the DIT content rule that we
    // are replacing (in which case we really do want to use the "!=" operator).
    ObjectClass structuralClass = ditContentRule.getStructuralClass();
    DITContentRule existingRuleForClass =
         schema.getDITContentRule(structuralClass);
    if ((existingRuleForClass != null) && (existingRuleForClass != existingDCR))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_DCR;
      String message = getMessage(msgID, ditContentRule.getName(),
                                  structuralClass.getNameOrOID(),
                                  existingRuleForClass.getName());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Make sure that the new DIT content rule doesn't reference an undefined
    // structural or auxiliaryclass, or an undefined required, optional, or
    // prohibited attribute type.
    if (! schema.hasObjectClass(structuralClass.getOID()))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_STRUCTURAL_OC;
      String message = getMessage(msgID, ditContentRule.getName(),
                                  structuralClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    if (structuralClass.getObjectClassType() != ObjectClassType.STRUCTURAL)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_DCR_OC_NOT_STRUCTURAL;
      String message = getMessage(msgID, ditContentRule.getName(),
                                  structuralClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    for (ObjectClass oc : ditContentRule.getAuxiliaryClasses())
    {
      if (! schema.hasObjectClass(oc.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_AUXILIARY_OC;
        String message = getMessage(msgID, ditContentRule.getName(),
                                    oc.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : ditContentRule.getRequiredAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_REQUIRED_ATTR;
        String message = getMessage(msgID, ditContentRule.getName(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : ditContentRule.getOptionalAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_OPTIONAL_ATTR;
        String message = getMessage(msgID, ditContentRule.getName(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    for (AttributeType at : ditContentRule.getProhibitedAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_PROHIBITED_ATTR;
        String message = getMessage(msgID, ditContentRule.getName(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If there is no existing rule, then we're adding a new DIT content rule.
    // Otherwise, we're replacing an existing one.
    if (existingDCR == null)
    {
      schema.registerDITContentRule(ditContentRule, false);
      String schemaFile = ditContentRule.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        ditContentRule.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterDITContentRule(existingDCR);
      schema.registerDITContentRule(ditContentRule, false);
      schema.rebuildDependentElements(existingDCR);
      if ((ditContentRule.getSchemaFile() == null) ||
          (ditContentRule.getSchemaFile().length() == 0))
      {
        String schemaFile = existingDCR.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        ditContentRule.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = ditContentRule.getSchemaFile();
        String oldSchemaFile = existingDCR.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided DIT content rule
   * from the server schema, ensuring all other metadata is properly updated.
   * Note that this method will first check to see whether the same rule will be
   * later added to the server schema with an updated definition, and if so then
   * the removal will be ignored because the later add will be handled as a
   * replace.  If the DIT content rule will not be replaced with a new
   * definition, then this method will ensure that there are no other schema
   * elements that depend on the rule before allowing it to be removed.
   *
   * @param  ditContentRule       The DIT content rule to remove from the server
   *                              schema.
   * @param  schema               The schema from which the DIT content rule
   *                              should be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided DIT content rule from the server
   *                              schema.
   */
  private void removeDITContentRule(DITContentRule ditContentRule,
                                    Schema schema,
                                    ArrayList<Modification> modifications,
                                    int currentPosition,
                                    Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeDITContentRule",
                      String.valueOf(ditContentRule), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified DIT content rule is actually defined in the server
    // schema.  If not, then fail.
    DITContentRule removeDCR =
         schema.getDITContentRule(ditContentRule.getStructuralClass());
    if ((removeDCR == null) || (! removeDCR.equals(ditContentRule)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DCR;
      String message = getMessage(msgID, ditContentRule.getName());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Since DIT content rules don't have any dependencies, then we don't need
    // to worry about the difference between a remove or a replace.  We can
    // just remove the DIT content rule now, and if it is added back later then
    // there still won't be any conflict.
    schema.deregisterDITContentRule(removeDCR);
    String schemaFile = removeDCR.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
  /**
   * Handles all processing required for adding the provided DIT structure rule
   * to the given schema, replacing an existing rule if necessary, and ensuring
   * all other metadata is properly updated.
   *
   * @param  ditStructureRule     The DIT structure rule to add or replace in
   *                              the server schema.
   * @param  schema               The schema to which the DIT structure rule
   *                              should be be added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided DIT structure rule to the server
   *                              schema.
   */
  private void addDITStructureRule(DITStructureRule ditStructureRule,
                                   Schema schema,
                                   Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addDITStructureRule",
                      String.valueOf(ditStructureRule), String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified DIT structure rule already exists.  We'll
    // check the rule ID and all of the names, which means that it's possible
    // there could be more than one match (although if there is, then we'll
    // refuse the operation).
    DITStructureRule existingDSR =
         schema.getDITStructureRule(ditStructureRule.getRuleID());
    for (DITStructureRule dsr : schema.getDITStructureRulesByID().values())
    {
      for (String name : ditStructureRule.getNames().keySet())
      {
        if (dsr.hasName(name))
        {
          // We really do want to use the "!=" operator here because it's
          // acceptable if we find match for the same object instance.
          if ((existingDSR != null) && (existingDSR != dsr))
          {
            int    msgID   = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DSR;
            String message = getMessage(msgID,
                                        ditStructureRule.getNameOrRuleID(),
                                        existingDSR.getNameOrRuleID(),
                                        dsr.getNameOrRuleID());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
        }
      }
    }
    // Get the name form for the new DIT structure rule and see if there's
    // already an existing rule that is associated with that name form.  If
    // there is, then it will only be acceptable if it's the DIT structure rule
    // that we are replacing (in which case we really do want to use the "!="
    // operator).
    NameForm nameForm = ditStructureRule.getNameForm();
    DITStructureRule existingRuleForNameForm =
         schema.getDITStructureRule(nameForm);
    if ((existingRuleForNameForm != null) &&
        (existingRuleForNameForm != existingDSR))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_NAME_FORM_CONFLICT_FOR_ADD_DSR;
      String message = getMessage(msgID, ditStructureRule.getNameOrRuleID(),
                                  nameForm.getNameOrOID(),
                                  existingRuleForNameForm.getNameOrRuleID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Make sure that the new DIT structure rule doesn't reference an undefined
    // name form or superior DIT structure rule.
    if (! schema.hasNameForm(nameForm.getOID()))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_DSR_UNDEFINED_NAME_FORM;
      String message = getMessage(msgID, ditStructureRule.getNameOrRuleID(),
                                  nameForm.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // If there is no existing rule, then we're adding a new DIT structure rule.
    // Otherwise, we're replacing an existing one.
    if (existingDSR == null)
    {
      schema.registerDITStructureRule(ditStructureRule, false);
      String schemaFile = ditStructureRule.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        ditStructureRule.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterDITStructureRule(existingDSR);
      schema.registerDITStructureRule(ditStructureRule, false);
      schema.rebuildDependentElements(existingDSR);
      if ((ditStructureRule.getSchemaFile() == null) ||
          (ditStructureRule.getSchemaFile().length() == 0))
      {
        String schemaFile = existingDSR.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        ditStructureRule.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = ditStructureRule.getSchemaFile();
        String oldSchemaFile = existingDSR.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided DIT structure rule
   * from the server schema, ensuring all other metadata is properly updated.
   * Note that this method will first check to see whether the same rule will be
   * later added to the server schema with an updated definition, and if so then
   * the removal will be ignored because the later add will be handled as a
   * replace.  If the DIT structure rule will not be replaced with a new
   * definition, then this method will ensure that there are no other schema
   * elements that depend on the rule before allowing it to be removed.
   *
   * @param  ditStructureRule     The DIT structure rule to remove from the
   *                              server schema.
   * @param  schema               The schema from which the DIT structure rule
   *                              should be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided DIT structure rule from the
   *                              server schema.
   */
  private void removeDITStructureRule(DITStructureRule ditStructureRule,
                                      Schema schema,
                                      ArrayList<Modification> modifications,
                                      int currentPosition,
                                      Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeDITStructureRule",
                      String.valueOf(ditStructureRule), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified DIT structure rule is actually defined in the server
    // schema.  If not, then fail.
    DITStructureRule removeDSR =
         schema.getDITStructureRule(ditStructureRule.getRuleID());
    if ((removeDSR == null) || (! removeDSR.equals(ditStructureRule)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DSR;
      String message = getMessage(msgID, ditStructureRule.getNameOrRuleID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // See if there is another modification later to add the DIT structure rule
    // back into the schema.  If so, then it's a replace and we should ignore
    // the remove because adding it back will handle the replace.
    for (int i=currentPosition+1; i < modifications.size(); i++)
    {
      Modification m = modifications.get(i);
      Attribute    a = m.getAttribute();
      if ((m.getModificationType() != ModificationType.ADD) ||
          (! a.getAttributeType().equals(ditStructureRulesType)))
      {
        continue;
      }
      for (AttributeValue v : a.getValues())
      {
        DITStructureRule dsr;
        try
        {
          dsr = DITStructureRuleSyntax.decodeDITStructureRule(
                     v.getValue(), schema, false);
        }
        catch (DirectoryException de)
        {
          assert debugException(CLASS_NAME, "removeDITStructureRule", de);
          int msgID = MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DSR;
          String message = getMessage(msgID, v.getStringValue(),
                                      de.getErrorMessage());
          throw new DirectoryException(
                         ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                         msgID, de);
        }
        if (ditStructureRule.getRuleID() == dsr.getRuleID())
        {
          // We found a match where the DIT structure rule is added back later,
          // so we don't need to do anything else here.
          return;
        }
      }
    }
    // Make sure that the DIT structure rule isn't the superior for any other
    // DIT structure rule.
    for (DITStructureRule dsr : schema.getDITStructureRulesByID().values())
    {
      if (dsr.getSuperiorRules().contains(removeDSR))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_DSR_SUPERIOR_RULE;
        String message = getMessage(msgID, removeDSR.getNameOrRuleID(),
                                    dsr.getNameOrRuleID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If we've gotten here, then it's OK to remove the DIT structure rule from
    // the schema.
    schema.deregisterDITStructureRule(removeDSR);
    String schemaFile = removeDSR.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
  /**
   * Handles all processing required for adding the provided matching rule use
   * to the given schema, replacing an existing use if necessary, and ensuring
   * all other metadata is properly updated.
   *
   * @param  matchingRuleUse      The matching rule use to add or replace in the
   *                              server schema.
   * @param  schema               The schema to which the matching rule use
   *                              should be added.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to add
   *                              the provided matching rule use to the server
   *                              schema.
   */
  private void addMatchingRuleUse(MatchingRuleUse matchingRuleUse,
                                  Schema schema,
                                  Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addMatchingRuleUse",
                      String.valueOf(matchingRuleUse), String.valueOf(schema),
                      String.valueOf(modifiedSchemaFiles));
    // First, see if the specified matching rule use already exists.  We'll
    // check all of the names, which means that it's possible that there could
    // be more than one match (although if there is, then we'll refuse the
    // operation).
    MatchingRuleUse existingMRU = null;
    for (MatchingRuleUse mru : schema.getMatchingRuleUses().values())
    {
      for (String name : matchingRuleUse.getNames().keySet())
      {
        if (mru.hasName(name))
        {
          if (existingMRU == null)
          {
            existingMRU = mru;
            break;
          }
          else
          {
            int msgID = MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_MR_USE;
            String message = getMessage(msgID, matchingRuleUse.getName(),
                                        existingMRU.getName(), mru.getName());
            throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                         message, msgID);
          }
        }
      }
    }
    // Get the matching rule for the new matching rule use and see if there's
    // already an existing matching rule use that is associated with that
    // matching rule.  If there is, then it will only be acceptable if it's the
    // matching rule use that we are replacing (in which case we really do want
    // to use the "!=" operator).
    MatchingRule matchingRule = matchingRuleUse.getMatchingRule();
    MatchingRuleUse existingMRUForRule =
         schema.getMatchingRuleUse(matchingRule);
    if ((existingMRUForRule != null) && (existingMRUForRule != existingMRU))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_MR_CONFLICT_FOR_ADD_MR_USE;
      String message = getMessage(msgID, matchingRuleUse.getName(),
                                  matchingRule.getNameOrOID(),
                                  existingMRUForRule.getName());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Make sure that the new matching rule use doesn't reference an undefined
    // attribute type.
    for (AttributeType at : matchingRuleUse.getAttributes())
    {
      if (! schema.hasAttributeType(at.getOID()))
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_MRU_UNDEFINED_ATTR;
        String message = getMessage(msgID, matchingRuleUse.getName(),
                                    at.getNameOrOID());
        throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                     msgID);
      }
    }
    // If there is no existing matching rule use, then we're adding a new one.
    // Otherwise, we're replacing an existing matching rule use.
    if (existingMRU == null)
    {
      schema.registerMatchingRuleUse(matchingRuleUse, false);
      String schemaFile = matchingRuleUse.getSchemaFile();
      if ((schemaFile == null) || (schemaFile.length() == 0))
      {
        schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        matchingRuleUse.setSchemaFile(schemaFile);
      }
      modifiedSchemaFiles.add(schemaFile);
    }
    else
    {
      schema.deregisterMatchingRuleUse(existingMRU);
      schema.registerMatchingRuleUse(matchingRuleUse, false);
      schema.rebuildDependentElements(existingMRU);
      if ((matchingRuleUse.getSchemaFile() == null) ||
          (matchingRuleUse.getSchemaFile().length() == 0))
      {
        String schemaFile = existingMRU.getSchemaFile();
        if ((schemaFile == null) || (schemaFile.length() == 0))
        {
          schemaFile = FILE_USER_SCHEMA_ELEMENTS;
        }
        matchingRuleUse.setSchemaFile(schemaFile);
        modifiedSchemaFiles.add(schemaFile);
      }
      else
      {
        String newSchemaFile = matchingRuleUse.getSchemaFile();
        String oldSchemaFile = existingMRU.getSchemaFile();
        if ((oldSchemaFile == null) || oldSchemaFile.equals(newSchemaFile))
        {
          modifiedSchemaFiles.add(newSchemaFile);
        }
        else
        {
          modifiedSchemaFiles.add(newSchemaFile);
          modifiedSchemaFiles.add(oldSchemaFile);
        }
      }
    }
  }
  /**
   * Handles all processing required to remove the provided matching rule use
   * from the server schema, ensuring all other metadata is properly updated.
   * Note that this method will first check to see whether the same matching
   * rule use will be later added to the server schema with an updated
   * definition, and if so then the removal will be ignored because the later
   * add will be handled as a replace.  If the matching rule use will not be
   * replaced with a new definition, then this method will ensure that there are
   * no other schema elements that depend on the matching rule use before
   * allowing it to be removed.
   *
   * @param  matchingRuleUse      The matching rule use to remove from the
   *                              server schema.
   * @param  schema               The schema from which the matching rule use
   *                              should be removed.
   * @param  modifications        The full set of modifications to be processed
   *                              against the server schema.
   * @param  currentPosition      The position of the modification currently
   *                              being performed.
   * @param  modifiedSchemaFiles  The names of the schema files containing
   *                              schema elements that have been updated as part
   *                              of the schema modification.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to remove
   *                              the provided matching rule use from the server
   *                              schema.
   */
  private void removeMatchingRuleUse(MatchingRuleUse matchingRuleUse,
                                     Schema schema,
                                     ArrayList<Modification> modifications,
                                     int currentPosition,
                                     Set<String> modifiedSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "removeMatchingRuleUse",
                      String.valueOf(matchingRuleUse), String.valueOf(schema),
                      String.valueOf(modifications),
                      String.valueOf(currentPosition),
                      String.valueOf(modifiedSchemaFiles));
    // See if the specified DIT content rule is actually defined in the server
    // schema.  If not, then fail.
    MatchingRuleUse removeMRU =
         schema.getMatchingRuleUse(matchingRuleUse.getMatchingRule());
    if ((removeMRU == null) || (! removeMRU.equals(matchingRuleUse)))
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_MR_USE;
      String message = getMessage(msgID, matchingRuleUse.getName());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    // Since matching rule uses don't have any dependencies, then we don't need
    // to worry about the difference between a remove or a replace.  We can
    // just remove the DIT content rule now, and if it is added back later then
    // there still won't be any conflict.
    schema.deregisterMatchingRuleUse(removeMRU);
    String schemaFile = removeMRU.getSchemaFile();
    if (schemaFile != null)
    {
      modifiedSchemaFiles.add(schemaFile);
    }
  }
@@ -1215,6 +2961,661 @@
  /**
   * Writes a temporary version of the specified schema file.
   *
   * @param  schema      The schema from which to take the definitions to be
   *                     written.
   * @param  schemaFile  The name of the schema file to be written.
   *
   * @throws  DirectoryException  If an unexpected problem occurs while
   *                              identifying the schema definitions to include
   *                              in the schema file.
   *
   * @throws  IOException  If an unexpected error occurs while attempting to
   *                       write the temporary schema file.
   *
   * @throws  LDIFException  If an unexpected problem occurs while generating
   *                         the LDIF representation of the schema entry.
   */
  private File writeTempSchemaFile(Schema schema, String schemaFile)
          throws DirectoryException, IOException, LDIFException
  {
    assert debugEnter(CLASS_NAME, "writeTempSchemaFile", String.valueOf(schema),
                      String.valueOf(schemaFile));
    // Start with an empty schema entry.
    Entry schemaEntry = createEmptySchemaEntry();
    // Add all of the appropriate attribute types to the schema entry.  We need
    // to be careful of the ordering to ensure that any superior types in the
    // same file are written before the subordinate types.
    HashSet<AttributeType> addedTypes = new HashSet<AttributeType>();
    LinkedHashSet<AttributeValue> values = new LinkedHashSet<AttributeValue>();
    for (AttributeType at : schema.getAttributeTypes().values())
    {
      if (schemaFile.equals(at.getSchemaFile()))
      {
        addAttrTypeToSchemaFile(schema, schemaFile, at, values, addedTypes, 0);
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(attributeTypesType,
                                 attributeTypesType.getPrimaryName(), values));
      schemaEntry.putAttribute(attributeTypesType, attrList);
    }
    // Add all of the appropriate objectclasses to the schema entry.  We need
    // to be careful of the ordering to ensure that any superior classes in the
    // same file are written before the subordinate classes.
    HashSet<ObjectClass> addedClasses = new HashSet<ObjectClass>();
    values = new LinkedHashSet<AttributeValue>();
    for (ObjectClass oc : schema.getObjectClasses().values())
    {
      if (schemaFile.equals(oc.getSchemaFile()))
      {
        addObjectClassToSchemaFile(schema, schemaFile, oc, values, addedClasses,
                                   0);
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(objectClassesType,
                                 objectClassesType.getPrimaryName(), values));
      schemaEntry.putAttribute(objectClassesType, attrList);
    }
    // Add all of the appropriate name forms to the schema entry.  Since there
    // is no hierarchical relationship between name forms, we don't need to
    // worry about ordering.
    values = new LinkedHashSet<AttributeValue>();
    for (NameForm nf : schema.getNameFormsByObjectClass().values())
    {
      if (schemaFile.equals(nf.getSchemaFile()))
      {
        values.add(new AttributeValue(nameFormsType, nf.getDefinition()));
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(nameFormsType,
                                 nameFormsType.getPrimaryName(), values));
      schemaEntry.putAttribute(nameFormsType, attrList);
    }
    // Add all of the appropriate DIT content rules to the schema entry.  Since
    // there is no hierarchical relationship between DIT content rules, we don't
    // need to worry about ordering.
    values = new LinkedHashSet<AttributeValue>();
    for (DITContentRule dcr : schema.getDITContentRules().values())
    {
      if (schemaFile.equals(dcr.getSchemaFile()))
      {
        values.add(new AttributeValue(ditContentRulesType,
                                      dcr.getDefinition()));
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(ditContentRulesType,
                                 ditContentRulesType.getPrimaryName(), values));
      schemaEntry.putAttribute(ditContentRulesType, attrList);
    }
    // Add all of the appropriate DIT structure rules to the schema entry.  We
    // need to be careful of the ordering to ensure that any superior rules in
    // the same file are written before the subordinate rules.
    HashSet<DITStructureRule> addedDSRs = new HashSet<DITStructureRule>();
    values = new LinkedHashSet<AttributeValue>();
    for (DITStructureRule dsr : schema.getDITStructureRulesByID().values())
    {
      if (schemaFile.equals(dsr.getSchemaFile()))
      {
        addDITStructureRuleToSchemaFile(schema, schemaFile, dsr, values,
                                        addedDSRs, 0);
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(ditStructureRulesType,
                                 ditStructureRulesType.getPrimaryName(),
                                 values));
      schemaEntry.putAttribute(ditStructureRulesType, attrList);
    }
    // Add all of the appropriate matching rule uses to the schema entry.  Since
    // there is no hierarchical relationship between matching rule uses, we
    // don't need to worry about ordering.
    values = new LinkedHashSet<AttributeValue>();
    for (MatchingRuleUse mru : schema.getMatchingRuleUses().values())
    {
      if (schemaFile.equals(mru.getSchemaFile()))
      {
        values.add(new AttributeValue(matchingRuleUsesType,
                                      mru.getDefinition()));
      }
    }
    if (! values.isEmpty())
    {
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(matchingRuleUsesType,
                                 matchingRuleUsesType.getPrimaryName(),
                                 values));
      schemaEntry.putAttribute(matchingRuleUsesType, attrList);
    }
    // Create a temporary file to which we can write the schema entry.
    File tempFile = File.createTempFile(schemaFile, "temp");
    LDIFExportConfig exportConfig =
         new LDIFExportConfig(tempFile.getAbsolutePath(),
                              ExistingFileBehavior.OVERWRITE);
    LDIFWriter ldifWriter = new LDIFWriter(exportConfig);
    ldifWriter.writeEntry(schemaEntry);
    ldifWriter.close();
    return tempFile;
  }
  /**
   * Adds the definition for the specified attribute type to the provided set of
   * attribute values, recursively adding superior types as appropriate.
   *
   * @param  schema         The schema containing the attribute type.
   * @param  schemaFile     The schema file with which the attribute type is
   *                        associated.
   * @param  attributeType  The attribute type whose definition should be added
   *                        to the value set.
   * @param  values         The set of values for attribute type definitions
   *                        already added.
   * @param  addedTypes     The set of attribute types whose definitions have
   *                        already been added to the set of values.
   * @param  depth          A depth counter to use in an attempt to detect
   *                        circular references.
   */
  private void addAttrTypeToSchemaFile(Schema schema, String schemaFile,
                                       AttributeType attributeType,
                                       LinkedHashSet<AttributeValue> values,
                                       HashSet<AttributeType> addedTypes,
                                       int depth)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addAttrTypeToSchemaFile",
                      String.valueOf(schema), String.valueOf(schemaFile),
                      String.valueOf(attributeType), String.valueOf(values),
                      String.valueOf(addedTypes), String.valueOf(depth));
    if (depth > 20)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_AT;
      String message = getMessage(msgID, attributeType.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    if (addedTypes.contains(attributeType))
    {
      return;
    }
    AttributeType superiorType = attributeType.getSuperiorType();
    if ((superiorType != null) &&
        schemaFile.equals(superiorType.getSchemaFile()) &&
        (! addedTypes.contains(superiorType)))
    {
      addAttrTypeToSchemaFile(schema, schemaFile, superiorType, values,
                              addedTypes, depth+1);
    }
    values.add(new AttributeValue(attributeTypesType,
                                  attributeType.getDefinition()));
    addedTypes.add(attributeType);
  }
  /**
   * Adds the definition for the specified objectclass to the provided set of
   * attribute values, recursively adding superior classes as appropriate.
   *
   * @param  schema        The schema containing the objectclass.
   * @param  schemaFile    The schema file with which the objectclass is
   *                       associated.
   * @param  objectClass   The objectclass whose definition should be added to
   *                       the value set.
   * @param  values        The set of values for objectclass definitions
   *                       already added.
   * @param  addedClasses  The set of objectclasses whose definitions have
   *                       already been added to the set of values.
   * @param  depth         A depth counter to use in an attempt to detect
   *                       circular references.
   */
  private void addObjectClassToSchemaFile(Schema schema, String schemaFile,
                                          ObjectClass objectClass,
                                          LinkedHashSet<AttributeValue> values,
                                          HashSet<ObjectClass> addedClasses,
                                          int depth)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addObjectClassToSchemaFile",
                      String.valueOf(schema), String.valueOf(schemaFile),
                      String.valueOf(objectClass), String.valueOf(values),
                      String.valueOf(addedClasses), String.valueOf(depth));
    if (depth > 20)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_OC;
      String message = getMessage(msgID, objectClass.getNameOrOID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    if (addedClasses.contains(objectClass))
    {
      return;
    }
    ObjectClass superiorClass = objectClass.getSuperiorClass();
    if ((superiorClass != null) &&
        schemaFile.equals(superiorClass.getSchemaFile()) &&
        (! addedClasses.contains(superiorClass)))
    {
      addObjectClassToSchemaFile(schema, schemaFile, superiorClass, values,
                                 addedClasses, depth+1);
    }
    values.add(new AttributeValue(objectClassesType,
                                  objectClass.getDefinition()));
    addedClasses.add(objectClass);
  }
  /**
   * Adds the definition for the specified DIT structure rule to the provided
   * set of attribute values, recursively adding superior rules as appropriate.
   *
   * @param  schema            The schema containing the DIT structure rule.
   * @param  schemaFile        The schema file with which the DIT structure rule
   *                           is associated.
   * @param  ditStructureRule  The DIT structure rule whose definition should be
   *                           added to the value set.
   * @param  values            The set of values for DIT structure rule
   *                           definitions already added.
   * @param  addedDSRs         The set of DIT structure rules whose definitions
   *                           have already been added added to the set of
   *                           values.
   * @param  depth             A depth counter to use in an attempt to detect
   *                           circular references.
   */
  private void addDITStructureRuleToSchemaFile(Schema schema, String schemaFile,
                    DITStructureRule ditStructureRule,
                    LinkedHashSet<AttributeValue> values,
                    HashSet<DITStructureRule> addedDSRs, int depth)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "addDITStructureRuleToSchemaFile",
                      String.valueOf(schema), String.valueOf(schemaFile),
                      String.valueOf(ditStructureRule), String.valueOf(values),
                      String.valueOf(addedDSRs), String.valueOf(depth));
    if (depth > 20)
    {
      int    msgID   = MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_DSR;
      String message = getMessage(msgID, ditStructureRule.getNameOrRuleID());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM, message,
                                   msgID);
    }
    if (addedDSRs.contains(ditStructureRule))
    {
      return;
    }
    for (DITStructureRule dsr : ditStructureRule.getSuperiorRules())
    {
      if (schemaFile.equals(dsr.getSchemaFile()) && (! addedDSRs.contains(dsr)))
      {
        addDITStructureRuleToSchemaFile(schema, schemaFile, dsr, values,
                                        addedDSRs, depth+1);
      }
    }
    values.add(new AttributeValue(ditStructureRulesType,
                                  ditStructureRule.getDefinition()));
    addedDSRs.add(ditStructureRule);
  }
  /**
   * Moves the specified temporary schema files in place of the active versions.
   * If an error occurs in the process, then this method will attempt to restore
   * the original schema files if possible.
   *
   * @param  tempSchemaFiles  The set of temporary schema files to be activated.
   *
   * @throws  DirectoryException  If a problem occurs while attempting to
   *                              install the temporary schema files.
   */
  private void installSchemaFiles(HashMap<String,File> tempSchemaFiles)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "installSchemaFiles",
                      String.valueOf(tempSchemaFiles));
    // Create lists that will hold the three types of files we'll be dealing
    // with (the temporary files that will be installed, the installed schema
    // files, and the previously-installed schema files).
    ArrayList<File> installedFileList = new ArrayList<File>();
    ArrayList<File> tempFileList      = new ArrayList<File>();
    ArrayList<File> origFileList      = new ArrayList<File>();
    File schemaDir = new File(SchemaConfigManager.getSchemaDirectoryPath());
    for (String name : tempSchemaFiles.keySet())
    {
      installedFileList.add(new File(schemaDir, name));
      tempFileList.add(tempSchemaFiles.get(name));
      origFileList.add(new File(schemaDir, name + ".orig"));
    }
    // If there are any old ".orig" files laying around from a previous
    // attempt, then try to clean them up.
    for (File f : origFileList)
    {
      if (f.exists())
      {
        f.delete();
      }
    }
    // Copy all of the currently-installed files with a ".orig" extension.  If
    // this fails, then try to clean up the copies.
    try
    {
      for (int i=0; i < installedFileList.size(); i++)
      {
        File installedFile = installedFileList.get(i);
        File origFile      = origFileList.get(i);
        if (installedFile.exists())
        {
          copyFile(installedFile, origFile);
        }
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "installSchemaFiles", e);
      boolean allCleaned = true;
      for (File f : origFileList)
      {
        try
        {
          if (f.exists())
          {
            if (! f.delete())
            {
              allCleaned = false;
            }
          }
        }
        catch (Exception e2)
        {
          assert debugException(CLASS_NAME, "installSchemaFiles", e2);
          allCleaned = false;
        }
      }
      if (allCleaned)
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_CLEANED;
        String message = getMessage(msgID, stackTraceToSingleLineString(e));
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, e);
      }
      else
      {
        int msgID = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_NOT_CLEANED;
        String message = getMessage(msgID, stackTraceToSingleLineString(e));
        DirectoryServer.sendAlertNotification(this,
                             ALERT_TYPE_CANNOT_COPY_SCHEMA_FILES, msgID,
                             message);
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, e);
      }
    }
    // Try to copy all of the temporary files into place over the installed
    // files.  If this fails, then try to restore the originals.
    try
    {
      for (int i=0; i < installedFileList.size(); i++)
      {
        File installedFile = installedFileList.get(i);
        File tempFile      = tempFileList.get(i);
        copyFile(tempFile, installedFile);
      }
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "installSchemaFiles", e);
      for (File f : installedFileList)
      {
        try
        {
          if (f.exists())
          {
            f.delete();
          }
        }
        catch (Exception e2)
        {
          assert debugException(CLASS_NAME, "installSchemaFiles", e2);
        }
      }
      boolean allRestored = true;
      for (int i=0; i < installedFileList.size(); i++)
      {
        File installedFile = installedFileList.get(i);
        File origFile      = origFileList.get(i);
        try
        {
          if (origFile.exists())
          {
            if (! origFile.renameTo(installedFile))
            {
              allRestored = false;
            }
          }
        }
        catch (Exception e2)
        {
          assert debugException(CLASS_NAME, "installSchemaFiles", e2);
          allRestored = false;
        }
      }
      if (allRestored)
      {
        int    msgID   = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_RESTORED;
        String message = getMessage(msgID, stackTraceToSingleLineString(e));
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, e);
      }
      else
      {
        int msgID = MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_NOT_RESTORED;
        String message = getMessage(msgID, stackTraceToSingleLineString(e));
        DirectoryServer.sendAlertNotification(this,
                             ALERT_TYPE_CANNOT_WRITE_NEW_SCHEMA_FILES, msgID,
                             message);
        throw new DirectoryException(DirectoryServer.getServerErrorResultCode(),
                                     message, msgID, e);
      }
    }
    // At this point, we're committed to the schema change, so we can't throw
    // any more exceptions, but all we have left is to clean up the original and
    // temporary files.
    for (File f : origFileList)
    {
      try
      {
        if (f.exists())
        {
          f.delete();
        }
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "installSchemaFiles", e);
      }
    }
    for (File f : tempFileList)
    {
      try
      {
        if (f.exists())
        {
          f.delete();
        }
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "installSchemaFiles", e);
      }
    }
  }
  /**
   * Creates a copy of the specified file.
   *
   * @param  from  The source file to be copied.
   * @param  to    The destination file to be created.
   *
   * @throws  IOException  If a problem occurs.
   */
  private void copyFile(File from, File to)
          throws IOException
  {
    assert debugEnter(CLASS_NAME, "copyFile", String.valueOf(from),
                      String.valueOf(to));
    byte[]           buffer        = new byte[4096];
    FileInputStream  inputStream   = null;
    FileOutputStream outputStream  = null;
    try
    {
      inputStream  = new FileInputStream(from);
      outputStream = new FileOutputStream(to, false);
      int bytesRead = inputStream.read(buffer);
      while (bytesRead > 0)
      {
        outputStream.write(buffer, 0, bytesRead);
        bytesRead = inputStream.read(buffer);
      }
    }
    finally
    {
      if (inputStream != null)
      {
        try
        {
          inputStream.close();
        }
        catch (Exception e)
        {
          assert debugException(CLASS_NAME, "copyFile", e);
        }
      }
      if (outputStream != null)
      {
        outputStream.close();
      }
    }
  }
  /**
   * Performs any necessary cleanup in an attempt to delete any temporary schema
   * files that may have been left over after trying to install the new schema.
   *
   * @param  tempSchemaFiles  The set of temporary schema files that have been
   *                          created and are candidates for cleanup.
   */
  private void cleanUpTempSchemaFiles(HashMap<String,File> tempSchemaFiles)
  {
    assert debugEnter(CLASS_NAME, "cleanUpTempSchemaFiles",
                      String.valueOf(tempSchemaFiles));
    if ((tempSchemaFiles == null) || tempSchemaFiles.isEmpty())
    {
      return;
    }
    for (File f : tempSchemaFiles.values())
    {
      try
      {
        if (f.exists())
        {
          f.delete();
        }
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "cleanUpTempSchemaFiles", e);
      }
    }
  }
  /**
   * Moves and/or renames the provided entry in this backend, altering any
   * subordinate entries as necessary.  This must ensure that an entry already
@@ -2795,5 +5196,63 @@
    this.showAllAttributes = showAllAttributes;
  }
  /**
   * Retrieves the DN of the configuration entry with which this alert generator
   * is associated.
   *
   * @return  The DN of the configuration entry with which this alert generator
   *          is associated.
   */
  public DN getComponentEntryDN()
  {
    assert debugEnter(CLASS_NAME, "getComponentEntryDN");
    return configEntryDN;
  }
  /**
   * Retrieves the fully-qualified name of the Java class for this alert
   * generator implementation.
   *
   * @return  The fully-qualified name of the Java class for this alert
   *          generator implementation.
   */
  public String getClassName()
  {
    assert debugEnter(CLASS_NAME, "getClassName");
    return CLASS_NAME;
  }
  /**
   * Retrieves information about the set of alerts that this generator may
   * produce.  The map returned should be between the notification type for a
   * particular notification and the human-readable description for that
   * notification.  This alert generator must not generate any alerts with types
   * that are not contained in this list.
   *
   * @return  Information about the set of alerts that this generator may
   *          produce.
   */
  public LinkedHashMap<String,String> getAlerts()
  {
    assert debugEnter(CLASS_NAME, "getAlerts");
    LinkedHashMap<String,String> alerts = new LinkedHashMap<String,String>();
    alerts.put(ALERT_TYPE_CANNOT_COPY_SCHEMA_FILES,
               ALERT_DESCRIPTION_CANNOT_COPY_SCHEMA_FILES);
    alerts.put(ALERT_TYPE_CANNOT_WRITE_NEW_SCHEMA_FILES,
               ALERT_DESCRIPTION_CANNOT_WRITE_NEW_SCHEMA_FILES);
    return alerts;
  }
}
opendj-sdk/opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
@@ -2905,10 +2905,16 @@
         directoryServer.schema.getObjectClass(TOP_OBJECTCLASS_NAME);
    if (objectClass == null)
    {
      objectClass = new ObjectClass(TOP_OBJECTCLASS_NAME, Collections
          .singleton(TOP_OBJECTCLASS_NAME), TOP_OBJECTCLASS_OID,
          TOP_OBJECTCLASS_DESCRIPTION, null, null, null,
          ObjectClassType.ABSTRACT, false, null);
      String definition =
           "( 2.5.6.0 NAME 'top' ABSTRACT MUST objectClass " +
           "X-ORIGIN 'RFC 2256' )";
      objectClass = new ObjectClass(definition, TOP_OBJECTCLASS_NAME,
                                    Collections.singleton(TOP_OBJECTCLASS_NAME),
                                    TOP_OBJECTCLASS_OID,
                                    TOP_OBJECTCLASS_DESCRIPTION, null, null,
                                    null, ObjectClassType.ABSTRACT, false,
                                    null);
    }
    return objectClass;
@@ -2937,10 +2943,13 @@
    ObjectClass objectClass = directoryServer.schema.getObjectClass(lowerName);
    if (objectClass == null)
    {
      objectClass = new ObjectClass(name,
          Collections.singleton(name), lowerName, null,
          getTopObjectClass(), null, null, ObjectClassType.ABSTRACT,
          false, null);
      String oid        = lowerName + "-oid";
      String definition = "( " + oid + " NAME '" + name + "' ABSTRACT )";
      objectClass = new ObjectClass(definition, name,
                                    Collections.singleton(name), oid, null,
                                    getTopObjectClass(), null, null,
                                    ObjectClassType.ABSTRACT, false, null);
    }
    return objectClass;
@@ -3109,8 +3118,12 @@
          }
        }
        String definition =
             "( 2.5.4.0 NAME 'objectClass' EQUALITY objectIdentifierMatch " +
             "SYNTAX 1.3.6.1.4.1.1466.115.121.1.38 X-ORIGIN 'RFC 2256' )";
        directoryServer.objectClassAttributeType =
             new AttributeType("objectClass",
             new AttributeType(definition, "objectClass",
                               Collections.singleton("objectClass"),
                               OBJECTCLASS_ATTRIBUTE_TYPE_OID, null, null,
                               oidSyntax, AttributeUsage.USER_APPLICATIONS,
@@ -3172,9 +3185,12 @@
                      String.valueOf(name));
    String lowerName = toLowerCase(name);
    return new AttributeType(name, Collections.singleton(name),
                             lowerName, null, null, syntax,
    String oid        = toLowerCase(name) + "-oid";
    String definition = "( " + oid + " NAME '" + name + "' SYNTAX " +
                        syntax.getOID() + " )";
    return new AttributeType(definition, name, Collections.singleton(name),
                             oid, null, null, syntax,
                             AttributeUsage.USER_APPLICATIONS, false, false,
                             false, false);
  }
opendj-sdk/opends/src/server/org/opends/server/core/SchemaConfigManager.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.core;
@@ -1247,7 +1247,8 @@
            DITStructureRule dsr;
            try
            {
              dsr = dsrSyntax.decodeDITStructureRule(v.getValue(), schema);
              dsr = dsrSyntax.decodeDITStructureRule(v.getValue(), schema,
                                                     false);
              dsr.getExtraProperties().remove(SCHEMA_PROPERTY_FILENAME);
              dsr.setSchemaFile(schemaFile);
            }
opendj-sdk/opends/src/server/org/opends/server/messages/BackendMessages.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.messages;
@@ -2364,9 +2364,8 @@
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to write an updated schema file.  This takes two arguments, which
   * are the path to the schema file and a string representation of the
   * exception that was caught.
   * trying to write an updated schema file.  This takes a single argument,
   * which is a string representation of the exception that was caught.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 222;
@@ -2374,6 +2373,595 @@
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to decode a new name form.  This takes two arguments, which
   * are the name form string and a message explaining the problem that
   * occurred.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 223;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to decode a new DIT content rule.  This takes two arguments,
   * which are the DIT content rule string and a message explaining the problem
   * that occurred.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DCR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 224;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to decode a new DIT structure rule.  This takes two arguments,
   * which are the DIT structure rule string and a message explaining the
   * problem that occurred.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DSR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 225;
  /**
   * The message ID for the message that will be used if an error occurs while
   * attempting to decode a new matching rule use.  This takes two arguments,
   * which are the matching rule use string and a message explaining the problem
   * that occurred.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_DECODE_MR_USE =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 226;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * remove all values for a given attribute type.  This takes a single
   * argument, which is the name of that attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_DELETE_NO_VALUES =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 227;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new attribute type that conflicts with multiple existing attribute
   * types.  This takes three arguments, which are the name or OID of the new
   * attribute type, and the name or OID of the two attribute types that were
   * found to conflict with the new type.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_ATTRTYPE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 228;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new attribute type that references an undefined superior attribute
   * type.  This takes two arguments, which are the name or OID of the new
   * attribute type and the name or OID of the superior attribute type.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_ATTRIBUTE_TYPE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 229;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new objectclas that conflicts with multiple existing objectclasses.
   * This takes three arguments, which are the name or OID of the new
   * objectclass, and the name or OID of the two objectclasses that were found
   * to conflict with the new objectclass.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_OBJECTCLASS =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 230;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that conflicts with multiple existing name forms.  This
   * takes three arguments, which are the name or OID of the new name form, and
   * the name or OID of the two name forms that were found to conflict with the
   * new name form.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_NAME_FORM =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 231;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that references an undefined structural objectclass.
   * This takes two arguments, which are the name or OID of the new name form
   * and the name or OID of the undefined objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_NF_UNDEFINED_STRUCTURAL_OC =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 232;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that references an undefined required attribute type.
   * This takes two arguments, which are the name or OID of the new name form
   * and the name or OID of the undefined attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_NF_UNDEFINED_REQUIRED_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 233;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that references an undefined optional attribute type.
   * This takes two arguments, which are the name or OID of the new name form
   * and the name or OID of the undefined attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_NF_UNDEFINED_OPTIONAL_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 234;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that conflicts with multiple existing DIT
   * content rules.  This takes three arguments, which are the name of the new
   * DIT content rule, and the names of the two DIT content rules that were
   * found to conflict with the new DIT content rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DCR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 235;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references a structural objectclass which
   * is already referenced by an existing DIT copntent rule.  This takes three
   * arguments, which are the name of the new DIT content rule, the name or OID
   * of the structural objectclass, and the name of the conflicting DIT content
   * rule.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_DCR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 236;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references a structural objectclass which
   * is not defined in the server schema.  This takes two arguments, which are
   * the name of the new DIT content rule and the name or OID of the undefined
   * objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_STRUCTURAL_OC =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 237;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references an auxiliary objectclass which
   * is not defined in the server schema.  This takes two arguments, which are
   * the name of the new DIT content rule and the name or OID of the undefined
   * objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_AUXILIARY_OC =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 238;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references a required attribute type
   * which is not defined in the server schema.  This takes two arguments, which
   * are the name of the new DIT content rule and the name or OID of the
   * undefined attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_REQUIRED_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 239;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references an optional attribute type
   * which is not defined in the server schema.  This takes two arguments, which
   * are the name of the new DIT content rule and the name or OID of the
   * undefined attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_OPTIONAL_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 240;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references a prohibited attribute type
   * which is not defined in the server schema.  This takes two arguments, which
   * are the name of the new DIT content rule and the name or OID of the
   * undefined attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_PROHIBITED_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 241;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT structure rule that conflicts with multiple existing DIT
   * structure rules.  This takes three arguments, which are the name or rule ID
   * of the new DIT structure rule, and the names or rule IDs of the two DIT
   * structure rules that were found to conflict with the new DIT structure
   * rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DSR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 242;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT structure rule that references a name form that is already
   * referenced by another DIT structure rule.  This takes three arguemnts,
   * which are the name or rule ID of the new DIT structure rule, the name or
   * OID of the name form, and the name or rule ID of the conflicting DIT
   * structure rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_NAME_FORM_CONFLICT_FOR_ADD_DSR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 243;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT structure rule that references a name form that is not
   * defined in the server schema.  This takes two arguments, which are the name
   * or rule ID of the new DIT structure rule and the name or OID of the
   * undefined name form.
   */
  public static final int MSGID_SCHEMA_MODIFY_DSR_UNDEFINED_NAME_FORM =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 244;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new matching rule use that conflicts with multiple existing matching
   * rule uses.  This takes three arguments, which are the name of the new
   * matching rule use, and the names of the two matching rule uses that were
   * found to conflict with the new matching rule use.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_MR_USE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 245;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new matching rule use that references a matching rule that is already
   * associated with another matching rule use.  This takes three arguments,
   * which are the name of the new matching rule use, the name or OID of the
   * matching rule, and the name of the conflicting matching rule use.
   */
  public static final int MSGID_SCHEMA_MODIFY_MR_CONFLICT_FOR_ADD_MR_USE =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 246;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new matching rule use that references an attribute type that is not
   * defined in the server schema.  This takes two arguments, which are the
   * name of the new matching rule use and the name or OID of the undefined
   * attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_MRU_UNDEFINED_ATTR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 247;
  /**
   * The message ID for the message that will be used if a circular reference
   * is detected in the superior chain for an attribute type.  This takes a
   * single argument, which is the name or OID of the attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_AT =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 248;
  /**
   * The message ID for the message that will be used if a circular reference
   * is detected in the superior chain for an objectclass.  This takes a single
   * argument, which is the name or OID of the objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_OC =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 249;
  /**
   * The message ID for the message that will be used if a circular reference
   * is detected in the superior chain for a DIT structure rule.  This takes a
   * single argument, which is the name or rule ID of the DIT structure rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_DSR =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 250;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to create copies of the current schema files, but the server was
   * able to properly clean up after itself.  This takes a single argument,
   * which is a string representation of the exception that was caught.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_CLEANED =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 251;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to create copies of the current schema files, and the server was
   * not able to properly clean up after itself.  This takes a single argument,
   * which is a string representation of the exception that was caught.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_NOT_CLEANED =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 252;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to write the new schema files, but the server was able to properly
   * clean up after itself.  This takes a single argument,  which is a string
   * representation of the exception that was caught.
   */
  public static final int MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_RESTORED =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 253;
  /**
   * The message ID for the message that will be used if an error occurs while
   * trying to write the new schema files, and the server was not able to
   * properly clean up after itself.  This takes a single argument, which is a
   * string representation of the exception that was caught.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_NOT_RESTORED =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 254;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because there is no such attribute
   * type defined.  This takes a single argument, which is the name or OID of
   * the attribute type to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_ATTRIBUTE_TYPE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 255;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because it is the superior type for
   * another attribute type.  This takes two arguments, which are the name or
   * OID of the attribute type to remove, and the name or OID of the subordinate
   * attribute type.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_AT_SUPERIOR_TYPE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 256;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because the attribute type is
   * referenced by an objectclass.  This takes two arguments, which are the name
   * or OID of the attribute type and the name or OID of the objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_OC =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 257;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because the attribute type is
   * referenced by a name form.  This takes two arguments, which are the name or
   * OID of the attribute type and the name or OID of the name form.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_NF =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 258;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because the attribute type is
   * referenced by a DIT content rule.  This takes two arguments, which are the
   * name or OID of the attribute type and the name of the DIT content rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_DCR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 259;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an attribute type from the schema fails because the attribute type is
   * referenced by a matching rule use.  This takes two arguments, which are the
   * name or OID of the attribute type and the name of the matching rule use.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_MR_USE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 260;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an objectclass from the schema fails because there is no such objectclass
   * defined.  This takes a single argument, which is the name or OID of the
   * objectclass to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_OBJECTCLASS =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 261;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an objectclass from the schema fails because it is the superior class for
   * another objectclass.  This takes two arguments, which are the name or OID
   * of the objectclass to remove, and the name or OID of the subordinate
   * objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_OC_SUPERIOR_CLASS =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 262;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an objectclass from the schema fails because the objectclass is referenced
   * by a name form.  This takes two arguments, which are the name or OID of the
   * objectclass and the name or OID of the name form.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_NF =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 263;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * an objectclass from the schema fails because the objectclass is referenced
   * by a DIT content rule.  This takes two arguments, which are the name or OID
   * of the objectclass and the name of the DIT content rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_DCR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 264;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a name form from the schema fails because there is no such name form
   * defined.  This takes a single argument, which is the name or OID of the
   * name form to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_NAME_FORM =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 265;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a name form from the schema fails because the name form is referenced by a
   * DIT structure rule.  This takes two arguments, which are the name or OID
   * of the name form and the name or rule ID of the DIT structure rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NF_IN_DSR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 266;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a DIT content rule from the schema fails because there is no such DIT
   * content rule defined.  This takes a single argument, which is the name of
   * the DIT content rule to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DCR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 267;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a DIT structure rule from the schema fails because there is no such DIT
   * structure rule defined.  This takes a single argument, which is the name or
   * rule ID of the DIT structure rule to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DSR =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 268;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a DIT structure rule from the schema fails because it is the superior rule
   * for another DIT structure rule.  This takes two arguments, which are the
   * name or rule ID of the DIT structure rule to remove, and the name or rule
   * ID of the subordinate rule.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_DSR_SUPERIOR_RULE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 269;
  /**
   * The message ID for the message that will be used if an attempt to remove
   * a matching rule use from the schema fails because there is no such matching
   * rule use defined.  This takes a single argument, which is the name of the
   * matching rule use to remove.
   */
  public static final int MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_MR_USE =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 270;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that references an objectclass that is not structural.
   * This takes two arguments, which are the name or OID of the new name form
   * and the name or OID of the objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_NF_OC_NOT_STRUCTURAL =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 271;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new DIT content rule that references an objectclass that is not
   * structural.  This takes two arguments, which are the name of the new DIT
   * content rule and the name or OID of the objectclass.
   */
  public static final int MSGID_SCHEMA_MODIFY_DCR_OC_NOT_STRUCTURAL =
       CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 272;
  /**
   * The message ID for the message that will be used if an attempt is made to
   * add a new name form that references a structural objectclass which is
   * already referenced by an existing name form.  This takes three arguments,
   * which are the name or OID of the new name form, the name or OID of the
   * structural objectclass, and the name or OID of the conflicting name form.
   */
  public static final int
       MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_NF =
            CATEGORY_MASK_BACKEND | SEVERITY_MASK_MILD_ERROR | 273;
  /**
   * Associates a set of generic messages with the message IDs defined in this
   * class.
   */
@@ -2609,12 +3197,9 @@
    registerMessage(MSGID_SCHEMA_DELETE_MODTYPE_NOT_SUPPORTED,
                    "The schema backend does not currently support removing " +
                    "existing schema elements.");
    // FIXME -- Change the below message once we support removing schema
    // elements.
    registerMessage(MSGID_SCHEMA_INVALID_MODIFICATION_TYPE,
                    "The schema backend does not support the %s modification " +
                    "type.  It is currently only possible to add new schema " +
                    "elements.");
                    "type.");
    registerMessage(MSGID_SCHEMA_MODIFY_UNSUPPORTED_ATTRIBUTE_TYPE,
                    "The schema backend does not support the modification of " +
                    "the %s attribute type.  Only attribute types, object " +
@@ -2630,6 +3215,21 @@
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_OBJECTCLASS,
                    "An error occurred while attempting to decode the object " +
                    "class \"%s\":  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_NAME_FORM,
                    "An error occurred while attempting to decode the name " +
                    "form \"%s\":  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DCR,
                    "An error occurred while attempting to decode the DIT " +
                    "content rule \"%s\":  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_DSR,
                    "An error occurred while attempting to decode the DIT " +
                    "structure rule \"%s\":  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_DECODE_MR_USE,
                    "An error occurred while attempting to decode the " +
                    "matching rule use \"%s\":  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_DELETE_NO_VALUES,
                    "The server will not allow removing all values for the " +
                    "%s attribute type in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_OBJECTCLASS,
                    "Unable to add objectclass %s because its superior " +
                    "class of %s is not defined in the server schema.");
@@ -2648,7 +3248,7 @@
                    "of schema file %s:  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_SCHEMA,
                    "An error occurred while attepting to write the updated " +
                    "schema file %s:  %s.");
                    "schema:  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_DN_NOT_SUPPORTED,
                    "Unwilling to rename entry \"%s\" because modify DN " +
                    "operations are not supported in the schema backend.");
@@ -2710,6 +3310,188 @@
                    "An error occurred while attempting to update the backup " +
                    "descriptor file %s with information about the schema " +
                    "backup:  %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_ATTRTYPE,
                    "Unable to add attribute type %s because it conflicts " +
                    "with multiple existing attribute types (%s and " +
                    "%s).");
    registerMessage(MSGID_SCHEMA_MODIFY_UNDEFINED_SUPERIOR_ATTRIBUTE_TYPE,
                    "Unable to add attribute type %s because it references " +
                    "superior attribute type %s which is not defined in the " +
                    "server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_OBJECTCLASS,
                    "Unable to add objectclass %s because it conflicts with " +
                    "multiple existing objectclasses (%s and %s).");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_NAME_FORM,
                    "Unable to add name form %s because it conflicts with " +
                    "multiple existing name forms (%s and %s).");
    registerMessage(MSGID_SCHEMA_MODIFY_NF_UNDEFINED_STRUCTURAL_OC,
                    "Unable to add name form %s because it references " +
                    "structural objectclass %s which is not defined in the " +
                    "server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_NF_OC_NOT_STRUCTURAL,
                    "Unable to add name form %s because it references " +
                    "objectclass %s which is defined in the server schema " +
                    "but is not a structural objectclass.");
    registerMessage(MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_NF,
                    "Unable to add name form %s because it references " +
                    "structural objectclass %s which is already associated " +
                    "with another name form %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_NF_UNDEFINED_REQUIRED_ATTR,
                    "Unable to add name form %s because it references " +
                    "required attribute type %s which is not defined in the " +
                    "server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_NF_UNDEFINED_OPTIONAL_ATTR,
                    "Unable to add name form %s because it references " +
                    "optional attribute type %s which is not defined in the " +
                    "server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DCR,
                    "Unable to add DIT content rule %s because it conflicts " +
                    "with multiple existing DIT content rules (%s and %s).");
    registerMessage(MSGID_SCHEMA_MODIFY_STRUCTURAL_OC_CONFLICT_FOR_ADD_DCR,
                    "Unable to add DIT content rule %s because it " +
                    "references structural objectclass %s which is already " +
                    "associated with another DIT content rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_STRUCTURAL_OC,
                    "Unable to add DIT content rule %s because it " +
                    "references structural objectclass %s which is not " +
                    "defined in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_OC_NOT_STRUCTURAL,
                    "Unable to add DIT content rule %s because it " +
                    "references structural objectclass %s which is defined " +
                    "in the server schema but is not structural.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_AUXILIARY_OC,
                    "Unable to add DIT content rule %s because it " +
                    "references auxiliary objectclass %s which is not " +
                    "defined in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_REQUIRED_ATTR,
                    "Unable to add DIT content rule %s because it " +
                    "references required attribute type %s which is not " +
                    "defined in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_OPTIONAL_ATTR,
                    "Unable to add DIT content rule %s because it " +
                    "references optional attribute type %s which is not " +
                    "defined in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_DCR_UNDEFINED_PROHIBITED_ATTR,
                    "Unable to add DIT content rule %s because it " +
                    "references prohibited attribute type %s which is not " +
                    "defined in the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_DSR,
                    "Unable to add DIT structure rule %s because it " +
                    "conflicts with multiple existing DIT structure rules " +
                    "(%s and %s).");
    registerMessage(MSGID_SCHEMA_MODIFY_NAME_FORM_CONFLICT_FOR_ADD_DSR,
                    "Unable to add DIT structure rule %s because it " +
                    "references name form %s which is already associated " +
                    "with another DIT structure rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_DSR_UNDEFINED_NAME_FORM,
                    "Unable to add DIT structure rule %s because it " +
                    "references name form %s which is not defined in the " +
                    "server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_MULTIPLE_CONFLICTS_FOR_ADD_MR_USE,
                    "Unable to add matching rule use %s because it " +
                    "conflicts with multiple existing matching rule uses " +
                    "(%s and %s).");
    registerMessage(MSGID_SCHEMA_MODIFY_MR_CONFLICT_FOR_ADD_MR_USE,
                    "Unable to add matching rule use %s because it " +
                    "references matching rule %s which is already associated " +
                    "with another matching rule use %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_MRU_UNDEFINED_ATTR,
                    "Unable to add matching rule use %s because it " +
                    "references attribute type %s which is not defined in " +
                    "the server schema.");
    registerMessage(MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_AT,
                    "Circular reference detected for attribute type %s in " +
                    "which the superior type chain references the " +
                    "attribute type itself.");
    registerMessage(MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_OC,
                    "Circular reference detected for objectclass %s in which " +
                    "the superior class chain references the objectclass " +
                    "itself.");
    registerMessage(MSGID_SCHEMA_MODIFY_CIRCULAR_REFERENCE_DSR,
                    "Circular reference detected for DIT structure rule %s " +
                    "in which the superior rule chain references the DIT " +
                    "structure rule itself.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_CLEANED,
                    "An error occurred while attempting to create copies " +
                    "of the existing schema files before applying the " +
                    "updates:  %s.  The server was able to restore the " +
                    "original schema configuration, so no additional " +
                    "cleanup should be required.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_ORIG_FILES_NOT_CLEANED,
                    "An error occurred while attempting to create copies " +
                    "of the existing schema files before applying the " +
                    "updates:  %s.  A problem also occurred when attempting " +
                    "to restore the original schema configuration, so the " +
                    "server may be left in an inconsistent state and could " +
                    "require manual cleanup.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_RESTORED,
                    "An error occurred while attempting to write new " +
                    "versions of the server schema files:  %s.   The server " +
                    "was able to restore the original schema configuration, " +
                    "so no additional cleanup should be required.");
    registerMessage(MSGID_SCHEMA_MODIFY_CANNOT_WRITE_NEW_FILES_NOT_RESTORED,
                    "An error occrred while attempting to write new " +
                    "versions of the server schema files:  %s.  A problem " +
                    "also occured when attempting to restore the original " +
                    "schema configuration, so the server may be left in an " +
                    "inconsistent state and could require manual cleanup.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_ATTRIBUTE_TYPE,
                    "Unable to remove attribute type %s from the server " +
                    "schema because no such attribute type is defined.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_AT_SUPERIOR_TYPE,
                    "Unable to remove attribute type %s from the server " +
                    "schema because it is referenced as the superior type " +
                    "for attribute type %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_OC,
                    "Unable to remove attribute type %s from the server " +
                    "schema because it is referenced as a required or " +
                    "optional attribute type in objectclass %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_NF,
                    "Unable to remove attribute type %s from the server " +
                    "schema because it is referenced as a required or " +
                    "optional attribute type in name form %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_DCR,
                    "Unable to remove attribute type %s from the server " +
                    "schema because it is referenced as a required, " +
                    "optional, or prohibited attribute type in DIT content " +
                    "rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_AT_IN_MR_USE,
                    "Unable to remove attribute type %s from the server " +
                    "schema because it is referenced by matching rule use %s");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_OBJECTCLASS,
                    "Unable to remove objectclass %s from the server schema " +
                    "because no such objectclass is defined.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_OC_SUPERIOR_CLASS,
                    "Unable to remove objectclass %s from the server schema " +
                    "because it is referenced as the superior class for " +
                    "objectclass %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_NF,
                    "Unable to remove objectclass %s from the server schema " +
                    "because it is referenced as the structural class for " +
                    "name form %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_OC_IN_DCR,
                    "Unable to remove objectclass %s from the server schema " +
                    "because it is referenced as a structural or auxiliary " +
                    "class for DIT content rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_NAME_FORM,
                    "Unable to remove name form %s from the server schema " +
                    "because no such name form is defined.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NF_IN_DSR,
                    "Unable to remove name form %s from the server schema " +
                    "because it is referenced by DIT structure rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DCR,
                    "Unable to remove DIT content rule %s from the server " +
                    "schema because no such DIT content rule is defined.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_DSR,
                    "Unable to remove DIT structure rule %s from the server " +
                    "schema because no such DIT structure rule is defined.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_DSR_SUPERIOR_RULE,
                    "Unable to remove DIT structure rule %s from the server " +
                    "schema because it is referenced as a superior rule for " +
                    "DIT structure rule %s.");
    registerMessage(MSGID_SCHEMA_MODIFY_REMOVE_NO_SUCH_MR_USE,
                    "Unable to remove matching rule use %s from the server " +
                    "schema because no such matching rule use is defined.");
    registerMessage(MSGID_SCHEMA_RESTORE_NO_SUCH_BACKUP,
opendj-sdk/opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.messages;
@@ -6057,6 +6057,16 @@
       CATEGORY_MASK_CORE | SEVERITY_MASK_SEVERE_WARNING | 578;
  /**
   * The message ID for the message that will be used if a circular reference is
   * detected when attempting to rebuild schema element dependencies.  This
   * takes a single argument, which is the definition string for the schema
   * element that triggered the circular reference error.
   */
  public static final int MSGID_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE =
       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 579;
  /**
   * Associates a set of generic messages with the message IDs defined
@@ -6751,6 +6761,11 @@
                    "Unable to register name form %s with the server schema " +
                    "because its name %s conflicts with the name for an " +
                    "existing name form %s.");
    registerMessage(MSGID_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE,
                    "Unable to update the schema element with definition " +
                    "\"%s\" because a circular reference was identified " +
                    "when attempting to rebuild other schema elements " +
                    "dependent upon it.");
    registerMessage(MSGID_ADD_OP_INVALID_SYNTAX,
opendj-sdk/opends/src/server/org/opends/server/plugins/EntryUUIDPlugin.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.plugins;
@@ -110,9 +110,15 @@
                                                        false);
    if (at == null)
    {
      at = new AttributeType(ENTRYUUID, Collections.singleton(ENTRYUUID),
                             ENTRYUUID, null, null,
                             DirectoryConfig.getDefaultAttributeSyntax(),
      String definition =
           "( 1.3.6.1.1.16.4 NAME 'entryUUID' DESC 'UUID of the entry' " +
           "EQUALITY uuidMatch ORDERING uuidOrderingMatch " +
           "SYNTAX 1.3.6.1.1.16.1 SINGLE-VALUE NO-USER-MODIFICATION " +
           "USAGE directoryOperation X-ORIGIN 'RFC 4530' )";
      at = new AttributeType(definition, ENTRYUUID,
                             Collections.singleton(ENTRYUUID), ENTRYUUID, null,
                             null, DirectoryConfig.getDefaultAttributeSyntax(),
                             AttributeUsage.DIRECTORY_OPERATION, false, true,
                             false, true);
    }
opendj-sdk/opends/src/server/org/opends/server/schema/AttributeTypeSyntax.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
@@ -926,12 +926,12 @@
    }
    return new AttributeType(primaryName, typeNames, oid, description,
                             superiorType, syntax, approximateMatchingRule,
                             equalityMatchingRule, orderingMatchingRule,
                             substringMatchingRule, attributeUsage,
                             isCollective, isNoUserModification, isObsolete,
                             isSingleValue, extraProperties);
    return new AttributeType(value.stringValue(), primaryName, typeNames, oid,
                             description, superiorType, syntax,
                             approximateMatchingRule, equalityMatchingRule,
                             orderingMatchingRule, substringMatchingRule,
                             attributeUsage, isCollective, isNoUserModification,
                             isObsolete, isSingleValue, extraProperties);
  }
opendj-sdk/opends/src/server/org/opends/server/schema/DITContentRuleSyntax.java
@@ -22,16 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
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 org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
@@ -503,20 +503,19 @@
    // 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.
    ConcurrentHashMap<String,String> names =
         new ConcurrentHashMap<String,String>();
    LinkedHashMap<String,String> names = new LinkedHashMap<String,String>();
    String description = null;
    boolean isObsolete = false;
    CopyOnWriteArraySet<ObjectClass> auxiliaryClasses =
         new CopyOnWriteArraySet<ObjectClass>();
    CopyOnWriteArraySet<AttributeType> requiredAttributes =
         new CopyOnWriteArraySet<AttributeType>();
    CopyOnWriteArraySet<AttributeType> optionalAttributes =
         new CopyOnWriteArraySet<AttributeType>();
    CopyOnWriteArraySet<AttributeType> prohibitedAttributes =
         new CopyOnWriteArraySet<AttributeType>();
    ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
         new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>();
    LinkedHashSet<ObjectClass> auxiliaryClasses =
         new LinkedHashSet<ObjectClass>();
    LinkedHashSet<AttributeType> requiredAttributes =
         new LinkedHashSet<AttributeType>();
    LinkedHashSet<AttributeType> optionalAttributes =
         new LinkedHashSet<AttributeType>();
    LinkedHashSet<AttributeType> prohibitedAttributes =
         new LinkedHashSet<AttributeType>();
    LinkedHashMap<String,List<String>> extraProperties =
         new LinkedHashMap<String,List<String>>();
    while (true)
@@ -943,16 +942,15 @@
        // 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>();
        LinkedList<String> valueList = new LinkedList<String>();
        pos = readExtraParameterValues(valueStr, valueList, pos);
        extraProperties.put(tokenName, valueList);
      }
    }
    return new DITContentRule(structuralClass, names, description,
                              auxiliaryClasses, requiredAttributes,
    return new DITContentRule(value.stringValue(), structuralClass, names,
                              description, auxiliaryClasses, requiredAttributes,
                              optionalAttributes, prohibitedAttributes,
                              isObsolete, extraProperties);
  }
@@ -1374,7 +1372,8 @@
   *                              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",
opendj-sdk/opends/src/server/org/opends/server/schema/DITStructureRuleSyntax.java
@@ -22,16 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
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 org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
@@ -281,7 +281,7 @@
    // acceptable.
    try
    {
      decodeDITStructureRule(value, DirectoryServer.getSchema());
      decodeDITStructureRule(value, DirectoryServer.getSchema(), true);
      return true;
    }
    catch (DirectoryException de)
@@ -302,10 +302,16 @@
   * should not be in order to allow the desired capitalization to be
   * preserved).
   *
   * @param  value   The ASN.1 octet string containing the value to decode (it
   *                 does not need to be normalized).
   * @param  schema  The schema to use to resolve references to other schema
   *                 elements.
   * @param  value                 The ASN.1 octet string containing the value
   *                               to decode (it does not need to be
   *                               normalized).
   * @param  schema                The schema to use to resolve references to
   *                               other schema elements.
   * @param  allowUnknownElements  Indicates whether to allow values that
   *                               reference a name form and/or superior rules
   *                               which are not defined in the server schema.
   *                               This should only be true when called by
   *                               {@code valueIsAcceptable}.
   *
   * @return  The decoded DIT structure rule definition.
   *
@@ -313,7 +319,8 @@
   *                              DIT structure rule definition.
   */
  public static DITStructureRule decodeDITStructureRule(ByteString value,
                                                        Schema schema)
                                      Schema schema,
                                      boolean allowUnknownElements)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "decodeDITStructureRule",
@@ -428,14 +435,14 @@
    // 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.
    ConcurrentHashMap<String,String> names =
         new ConcurrentHashMap<String,String>();
    LinkedHashMap<String,String> names = new LinkedHashMap<String,String>();
    String description = null;
    boolean isObsolete = false;
    NameForm nameForm = null;
    CopyOnWriteArraySet<DITStructureRule> superiorRules = null;
    ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
         new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>();
    boolean nameFormGiven = false;
    LinkedHashSet<DITStructureRule> superiorRules = null;
    LinkedHashMap<String,List<String>> extraProperties =
         new LinkedHashMap<String,List<String>>();
    while (true)
@@ -533,8 +540,9 @@
        StringBuilder woidBuffer = new StringBuilder();
        pos = readWOID(lowerStr, woidBuffer, pos);
        nameFormGiven = true;
        nameForm = schema.getNameForm(woidBuffer.toString());
        if (nameForm == null)
        if ((nameForm == null) && (! allowUnknownElements))
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DSR_UNKNOWN_NAME_FORM;
          String message = getMessage(msgID, valueStr, woidBuffer.toString());
@@ -605,10 +613,14 @@
                 schema.getDITStructureRule(supRuleID);
            if (superiorRule == null)
            {
              int msgID = MSGID_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID;
              String message = getMessage(msgID, valueStr, supRuleID);
              throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                           message, msgID);
              if (! allowUnknownElements)
              {
                int    msgID   = MSGID_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID;
                String message = getMessage(msgID, valueStr, supRuleID);
                throw new DirectoryException(
                               ResultCode.INVALID_ATTRIBUTE_SYNTAX, message,
                               msgID);
              }
            }
            else
            {
@@ -698,10 +710,13 @@
          DITStructureRule superiorRule = schema.getDITStructureRule(supRuleID);
          if (superiorRule == null)
          {
            int msgID = MSGID_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID;
            String message = getMessage(msgID, valueStr, supRuleID);
            throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                         message, msgID);
            if (! allowUnknownElements)
            {
              int    msgID   = MSGID_ATTR_SYNTAX_DSR_UNKNOWN_RULE_ID;
              String message = getMessage(msgID, valueStr, supRuleID);
              throw new DirectoryException(ResultCode.INVALID_ATTRIBUTE_SYNTAX,
                                           message, msgID);
            }
          }
          else
          {
@@ -724,7 +739,7 @@
          }
        }
        superiorRules = new CopyOnWriteArraySet<DITStructureRule>(superiorList);
        superiorRules = new LinkedHashSet<DITStructureRule>(superiorList);
      }
      else
      {
@@ -732,15 +747,15 @@
        // 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>();
        LinkedList<String> valueList =
             new LinkedList<String>();
        pos = readExtraParameterValues(valueStr, valueList, pos);
        extraProperties.put(tokenName, valueList);
      }
    }
    if (nameForm == null)
    if ((nameForm == null) && (! nameFormGiven))
    {
      int    msgID   = MSGID_ATTR_SYNTAX_DSR_NO_NAME_FORM;
      String message = getMessage(msgID, valueStr);
@@ -749,8 +764,9 @@
    }
    return new DITStructureRule(names, ruleID, description, isObsolete,
                                nameForm, superiorRules, extraProperties);
    return new DITStructureRule(value.stringValue(), names, ruleID, description,
                                isObsolete, nameForm, superiorRules,
                                extraProperties);
  }
@@ -1170,7 +1186,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",
opendj-sdk/opends/src/server/org/opends/server/schema/MatchingRuleUseSyntax.java
@@ -22,14 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
import java.util.*;
import java.util.concurrent.*;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
@@ -490,13 +492,12 @@
    // 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.
    ConcurrentHashMap<String,String> names =
         new ConcurrentHashMap<String,String>();
    LinkedHashMap<String,String> names = new LinkedHashMap<String,String>();
    String description = null;
    boolean isObsolete = false;
    CopyOnWriteArraySet<AttributeType> attributes = null;
    ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
         new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>();
    LinkedHashSet<AttributeType> attributes = null;
    LinkedHashMap<String,List<String>> extraProperties =
         new LinkedHashMap<String,List<String>>();
    while (true)
    {
@@ -664,7 +665,7 @@
          attrs.add(attr);
        }
        attributes = new CopyOnWriteArraySet<AttributeType>(attrs);
        attributes = new LinkedHashSet<AttributeType>(attrs);
      }
      else
      {
@@ -672,8 +673,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>();
        LinkedList<String> valueList = new LinkedList<String>();
        pos = readExtraParameterValues(valueStr, valueList, pos);
        extraProperties.put(tokenName, valueList);
      }
@@ -690,8 +690,9 @@
    }
    return new MatchingRuleUse(matchingRule, names, description, isObsolete,
                               attributes, extraProperties);
    return new MatchingRuleUse(value.stringValue(), matchingRule, names,
                               description, isObsolete, attributes,
                               extraProperties);
  }
@@ -1110,7 +1111,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",
opendj-sdk/opends/src/server/org/opends/server/schema/NameFormSyntax.java
@@ -22,16 +22,16 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
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 org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
@@ -478,17 +478,16 @@
    // 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.
    ConcurrentHashMap<String,String> names =
         new ConcurrentHashMap<String,String>();
    LinkedHashMap<String,String> names = new LinkedHashMap<String,String>();
    String description = null;
    boolean isObsolete = false;
    ObjectClass structuralClass = null;
    CopyOnWriteArraySet<AttributeType> requiredAttributes =
         new CopyOnWriteArraySet<AttributeType>();
    CopyOnWriteArraySet<AttributeType> optionalAttributes =
         new CopyOnWriteArraySet<AttributeType>();
    ConcurrentHashMap<String,CopyOnWriteArrayList<String>> extraProperties =
         new ConcurrentHashMap<String,CopyOnWriteArrayList<String>>();
    LinkedHashSet<AttributeType> requiredAttributes =
         new LinkedHashSet<AttributeType>();
    LinkedHashSet<AttributeType> optionalAttributes =
         new LinkedHashSet<AttributeType>();
    LinkedHashMap<String,List<String>> extraProperties =
         new LinkedHashMap<String,List<String>>();
    while (true)
@@ -775,8 +774,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>();
        LinkedList<String> valueList = new LinkedList<String>();
        pos = readExtraParameterValues(valueStr, valueList, pos);
        extraProperties.put(tokenName, valueList);
      }
@@ -794,9 +792,9 @@
    }
    return new NameForm(names, oid, description, isObsolete, structuralClass,
                        requiredAttributes, optionalAttributes,
                        extraProperties);
    return new NameForm(value.stringValue(), names, oid, description,
                        isObsolete, structuralClass, requiredAttributes,
                        optionalAttributes, extraProperties);
  }
@@ -1215,7 +1213,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",
opendj-sdk/opends/src/server/org/opends/server/schema/ObjectClassSyntax.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.schema;
@@ -807,9 +807,10 @@
    }
    return new ObjectClass(primaryName, names, oid, description, superiorClass,
                           requiredAttributes, optionalAttributes,
                           objectClassType, isObsolete, extraProperties);
    return new ObjectClass(value.stringValue(), primaryName, names, oid,
                           description, superiorClass, requiredAttributes,
                           optionalAttributes, objectClassType, isObsolete,
                           extraProperties);
  }
opendj-sdk/opends/src/server/org/opends/server/types/AttributeType.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
@@ -31,17 +31,20 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.opends.server.api.ApproximateMatchingRule;
import org.opends.server.api.AttributeSyntax;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.api.SubstringMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.schema.AttributeTypeSyntax;
import static org.opends.server.loggers.Debug.*;
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.Validator.*;
@@ -60,7 +63,9 @@
 * ordering will be preserved when the associated fields are accessed
 * via their getters or via the {@link #toString()} methods.
 */
public final class AttributeType extends CommonSchemaElements
public final class AttributeType
       extends CommonSchemaElements
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -102,6 +107,9 @@
  // The ordering matching rule for this attribute type.
  private final OrderingMatchingRule orderingMatchingRule;
  // The definition string used to create this attribute type.
  private final String definition;
  // The substring matching rule for this attribute type.
  private final SubstringMatchingRule substringMatchingRule;
@@ -115,6 +123,9 @@
   * from the set of <code>names</code> will be used as the primary
   * name.
   *
   * @param definition
   *          The definition string used to create this attribute
   *          type.  It must not be {@code null}.
   * @param primaryName
   *          The primary name for this attribute type, or
   *          <code>null</code> if there is no primary name.
@@ -122,8 +133,8 @@
   *          The full set of names for this attribute type, or
   *          <code>null</code> if there are no names.
   * @param oid
   *          The OID for this attribute type (must not be
   *          <code>null</code>).
   *          The OID for this attribute type.  It must not be
   *          {@code null}.
   * @param description
   *          The description for the attribute type, or
   *          <code>null</code> if there is no description.
@@ -150,7 +161,7 @@
   *          Indicates whether this attribute type is declared
   *          "single-value".
   */
  public AttributeType(String primaryName,
  public AttributeType(String definition, String primaryName,
                       Collection<String> typeNames,
                       String oid, String description,
                       AttributeType superiorType,
@@ -160,7 +171,7 @@
                       boolean isNoUserModification,
                       boolean isObsolete, boolean isSingleValue)
  {
    this(primaryName, typeNames, oid, description,
    this(definition, primaryName, typeNames, oid, description,
        superiorType, syntax, null, null, null,
        null, attributeUsage, isCollective,
        isNoUserModification, isObsolete, isSingleValue, null);
@@ -176,6 +187,9 @@
   * from the set of <code>names</code> will be used as the primary
   * name.
   *
   * @param definition
   *          The definition string used to create this attribute
   *          type.  It must not be {@code null}.
   * @param primaryName
   *          The primary name for this attribute type, or
   *          <code>null</code> if there is no primary name.
@@ -183,8 +197,8 @@
   *          The full set of names for this attribute type, or
   *          <code>null</code> if there are no names.
   * @param oid
   *          The OID for this attribute type (must not be
   *          <code>null</code>).
   *          The OID for this attribute type.  It must not be
   *          {@code null}.
   * @param description
   *          The description for the attribute type, or
   *          <code>null</code> if there is no description.
@@ -225,10 +239,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,
  public AttributeType(String definition, String primaryName,
                       Collection<String> typeNames,
                       String oid, String description,
                       AttributeType superiorType,
@@ -243,12 +255,13 @@
                       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),
    assert debugConstructor(CLASS_NAME,
                              String.valueOf(definition),
                              String.valueOf(primaryName),
                              String.valueOf(typeNames),
                              String.valueOf(oid),
                              String.valueOf(description),
@@ -265,6 +278,9 @@
                              String.valueOf(isSingleValue),
                              String.valueOf(extraProperties));
    ensureNotNull(definition, oid);
    this.definition   = definition;
    this.superiorType = superiorType;
    this.isCollective = isCollective;
    this.isNoUserModification = isNoUserModification;
@@ -352,6 +368,51 @@
  /**
   * Retrieves the definition string used to create this attribute
   * type.
   *
   * @return  The definition string used to create this attribute
   *          type.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this attribute type based on the
   * definition string.  It will also preserve other state information
   * associated with this attribute type that is not included in the
   * definition string (e.g., the name of the schema file with which
   * it is associated).
   *
   * @return  The new instance of this attribute type based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new attribute type
   *                              instance from the definition string.
   */
  public AttributeType recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryServer.getSchema();
    AttributeType at =
         AttributeTypeSyntax.decodeAttributeType(value, schema);
    at.setSchemaFile(getSchemaFile());
    return at;
  }
  /**
   * Retrieves the superior type for this attribute type.
   *
   * @return  The superior type for this attribute type, or
opendj-sdk/opends/src/server/org/opends/server/types/CommonSchemaElements.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
@@ -32,10 +32,12 @@
import static org.opends.server.loggers.Debug.debugEnter;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.toLowerCase;
import static org.opends.server.util.Validator.*;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -59,6 +61,12 @@
 * 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.
 * <p>
 * Note that these schema elements are not completely immutable, as
 * the set of extra properties for the schema element may be altered
 * after the element is created.  Among other things, this allows the
 * associated schema file to be edited so that an element created over
 * protocol may be associated with a particular schema file.
 */
public abstract class CommonSchemaElements {
  /**
@@ -316,10 +324,10 @@
  /**
   * Retrieves the path to the schema file that contains the
   * Retrieves the name of the schema file that contains the
   * definition for this schema definition.
   *
   * @return The path to the schema file that contains the definition
   * @return The name of 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.
   */
@@ -338,6 +346,25 @@
  /**
   * Specifies the name of the schema file that contains the
   * definition for this schema element.  If a schema file is already
   * defined in the set of extra properties, then it will be
   * overwritten.  If the provided schema file value is {@code null},
   * then any existing schema file definition will be removed.
   *
   * @param  schemaFile  The name of the schema file that contains the
   *                     definition for this schema element.
   */
  public final void setSchemaFile(String schemaFile) {
    assert debugEnter(CLASS_NAME, "setSchemaFile",
                      String.valueOf(schemaFile));
    setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
  }
  /**
   * Retrieves the description for this schema definition.
   *
   * @return The description for this schema definition, or
@@ -401,6 +428,72 @@
  /**
   * Sets the value for an "extra" property for this schema element.
   * If a property already exists with the specified name, then it
   * will be overwritten.  If the value is {@code null}, then any
   * existing property with the given name will be removed.
   *
   * @param  name   The name for the "extra" property.  It must not be
   *                {@code null}.
   * @param  value  The value for the "extra" property.  If it is
   *                {@code null}, then any existing definition will be
   *                removed.
   */
  public final void setExtraProperty(String name, String value) {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(value));
    ensureNotNull(name);
    if (value == null)
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> values = new LinkedList<String>();
      values.add(value);
      extraProperties.put(name, values);
    }
  }
  /**
   * Sets the values for an "extra" property for this schema element.
   * If a property already exists with the specified name, then it
   * will be overwritten.  If the set of values is {@code null} or
   * empty, then any existing property with the given name will be
   * removed.
   *
   * @param  name    The name for the "extra" property.  It must not
   *                 be {@code null}.
   * @param  values  The set of values for the "extra" property.  If
   *                 it is {@code null} or empty, then any existing
   *                 definition will be removed.
   */
  public final void setExtraProperty(String name,
                                     List<String> values) {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(values));
    ensureNotNull(name);
    if ((values == null) || values.isEmpty())
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> valuesCopy = new LinkedList<String>(values);
      extraProperties.put(name, valuesCopy);
    }
  }
  /**
   * 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.
opendj-sdk/opends/src/server/org/opends/server/types/DITContentRule.java
@@ -22,16 +22,21 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.schema.DITContentRuleSyntax;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.loggers.Error.*;
@@ -39,6 +44,7 @@
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -48,7 +54,8 @@
 * given structural objectclass, and also indicates which auxiliary
 * classes that may be included in the entry.
 */
public class DITContentRule
public final class DITContentRule
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -59,40 +66,38 @@
  // Indicates whether this content rule is declared "obsolete".
  private boolean isObsolete;
  private final boolean isObsolete;
  // The set of additional name-value pairs associated with this
  // content rule definition.
  private ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
               extraProperties;
  private final Map<String,List<String>> extraProperties;
  // The set of names for this DIT content rule, in a mapping between
  // the all-lowercase form and the user-defined form.
  private ConcurrentHashMap<String,String> names;
  private final Map<String,String> names;
  // The structural objectclass for this DIT content rule.
  private final ObjectClass structuralClass;
  // The set of auxiliary objectclasses that entries with this content
  // rule may contain, in a mapping between the objectclass and the
  // user-defined name for that class.
  private CopyOnWriteArraySet<ObjectClass> auxiliaryClasses;
  private final Set<ObjectClass> auxiliaryClasses;
  // The set of optional attribute types for this DIT content rule.
  private CopyOnWriteArraySet<AttributeType> optionalAttributes;
  private final Set<AttributeType> optionalAttributes;
  // The set of prohibited attribute types for this DIT content rule.
  private CopyOnWriteArraySet<AttributeType> prohibitedAttributes;
  private final Set<AttributeType> prohibitedAttributes;
  // The set of required attribute types for this DIT content rule.
  private CopyOnWriteArraySet<AttributeType> requiredAttributes;
  private final Set<AttributeType> requiredAttributes;
  // The structural objectclass for this DIT content rule.
  private ObjectClass structuralClass;
  // The definition string used to create this DIT content rule.
  private final String definition;
  // The description for this attribute type.
  private String description;
  // The path to the schema file that contains this DIT content rule
  // definition.
  private String schemaFile;
  // The description for this DIT content rule.
  private final String description;
@@ -100,8 +105,12 @@
   * Creates a new DIT content rule definition with the provided
   * information.
   *
   * @param  definition            The definition string used to
   *                               create this DIT content rule.  It
   *                               must not be {@code null}.
   * @param  structuralClass       The structural objectclass for this
   *                               DIT content rule.
   *                               DIT content rule.  It must not be
   *                               {@code null}.
   * @param  names                 The set of names that may be used
   *                               to reference this DIT content rule.
   * @param  description           The description for this DIT
@@ -119,41 +128,139 @@
   * @param  extraProperties       A set of extra properties for this
   *                               DIT content rule.
   */
  public DITContentRule(ObjectClass structuralClass,
              ConcurrentHashMap<String,String> names,
              String description,
              CopyOnWriteArraySet<ObjectClass> auxiliaryClasses,
              CopyOnWriteArraySet<AttributeType> requiredAttributes,
              CopyOnWriteArraySet<AttributeType> optionalAttributes,
              CopyOnWriteArraySet<AttributeType> prohibitedAttributes,
              boolean isObsolete,
              ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
                   extraProperties)
  public DITContentRule(String definition,
                        ObjectClass structuralClass,
                        Map<String,String> names, String description,
                        Set<ObjectClass> auxiliaryClasses,
                        Set<AttributeType> requiredAttributes,
                        Set<AttributeType> optionalAttributes,
                        Set<AttributeType> prohibitedAttributes,
                        boolean isObsolete,
                        Map<String,List<String>> extraProperties)
  {
    assert debugConstructor(CLASS_NAME,
                            new String[]
                            {
                              String.valueOf(structuralClass),
                              String.valueOf(names),
                              String.valueOf(description),
                              String.valueOf(auxiliaryClasses),
                              String.valueOf(requiredAttributes),
                              String.valueOf(optionalAttributes),
                              String.valueOf(prohibitedAttributes),
                              String.valueOf(isObsolete),
                              String.valueOf(extraProperties)
                            });
    assert debugConstructor(CLASS_NAME, String.valueOf(definition),
                            String.valueOf(structuralClass),
                            String.valueOf(names),
                            String.valueOf(description),
                            String.valueOf(auxiliaryClasses),
                            String.valueOf(requiredAttributes),
                            String.valueOf(optionalAttributes),
                            String.valueOf(prohibitedAttributes),
                            String.valueOf(isObsolete),
                            String.valueOf(extraProperties));
    this.structuralClass      = structuralClass;
    this.names                = names;
    this.description          = description;
    this.auxiliaryClasses     = auxiliaryClasses;
    this.requiredAttributes   = requiredAttributes;
    this.optionalAttributes   = optionalAttributes;
    this.prohibitedAttributes = prohibitedAttributes;
    this.isObsolete           = isObsolete;
    this.schemaFile           = null;
    this.extraProperties      = extraProperties;
    ensureNotNull(definition, structuralClass);
    this.definition      = definition;
    this.structuralClass = structuralClass;
    this.description     = description;
    this.isObsolete      = isObsolete;
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
    }
    else
    {
      this.names = new LinkedHashMap<String,String>(names);
    }
    if ((auxiliaryClasses == null) || auxiliaryClasses.isEmpty())
    {
      this.auxiliaryClasses = new LinkedHashSet<ObjectClass>(0);
    }
    else
    {
      this.auxiliaryClasses =
           new LinkedHashSet<ObjectClass>(auxiliaryClasses);
    }
    if ((requiredAttributes == null) || requiredAttributes.isEmpty())
    {
      this.requiredAttributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.requiredAttributes =
           new LinkedHashSet<AttributeType>(requiredAttributes);
    }
    if ((optionalAttributes == null) || optionalAttributes.isEmpty())
    {
      this.optionalAttributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.optionalAttributes =
           new LinkedHashSet<AttributeType>(optionalAttributes);
    }
    if ((prohibitedAttributes == null) ||
        prohibitedAttributes.isEmpty())
    {
      this.prohibitedAttributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.prohibitedAttributes =
           new LinkedHashSet<AttributeType>(prohibitedAttributes);
    }
    if ((extraProperties == null) || extraProperties.isEmpty())
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(0);
    }
    else
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(extraProperties);
    }
  }
  /**
   * Retrieves the definition string used to create this DIT content
   * rule.
   *
   * @return  The definition string used to create this DIT content
   *          rule.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this DIT content rule based on the
   * definition string.  It will also preserve other state information
   * associated with this DIT content rule that is not included in the
   * definition string (e.g., the name of the schema file with which
   * it is associated).
   *
   * @return  The new instance of this DIT content rule based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new DIT content rule
   *                              instance from the definition string.
   */
  public DITContentRule recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryConfig.getSchema();
    DITContentRule dcr =
         DITContentRuleSyntax.decodeDITContentRule(value, schema);
    dcr.setSchemaFile(getSchemaFile());
    return dcr;
  }
@@ -173,22 +280,6 @@
  /**
   * Specifies the structural objectclass for this DIT content rule.
   *
   * @param  structuralClass  The structural objectclass for this DIT
   *                          content rule.
   */
  public void setStructuralClass(ObjectClass structuralClass)
  {
    assert debugEnter(CLASS_NAME, "setStructuralClass",
                      String.valueOf(structuralClass));
    this.structuralClass = structuralClass;
  }
  /**
   * Retrieves the set of names that may be used to reference this DIT
   * content rule.  The returned object will be a mapping between each
   * name in all lowercase characters and that name in a user-defined
@@ -197,7 +288,7 @@
   * @return  The set of names that may be used to reference this DIT
   *          content rule.
   */
  public ConcurrentHashMap<String,String> getNames()
  public Map<String,String> getNames()
  {
    assert debugEnter(CLASS_NAME, "getNames");
@@ -211,7 +302,7 @@
   * rule.
   *
   * @return  The primary name to use to reference this DIT content
   *          rule, or <CODE>null</CODE> if there is none.
   *          rule, or {@code null} if there is none.
   */
  public String getName()
  {
@@ -230,33 +321,15 @@
  /**
   * Specifies the set of names that may be used to reference this DIT
   * content rule.  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 DIT content rule.
   */
  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 DIT content rule.
   *
   * @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 DIT content rule, or
   *          <CODE>false</CODE> if not.
   * @return  {@code true} if the provided lowercase name may be used
   *          to reference this DIT content rule, or {@code false} if
   *          not.
   */
  public boolean hasName(String lowerName)
  {
@@ -269,62 +342,34 @@
  /**
   * Adds the provided name to the set of names that may be used to
   * reference this DIT content rule.
   *
   * @param  name  The name to add to the set of names that may be
   *               used to reference this DIT content rule.
   */
  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 DIT content rule.
   *
   * @param  lowerName  The name to remove from the set of names that
   *                    may be used to reference this DIT content
   *                    rule, in all lowercase characters.
   */
  public void removeName(String lowerName)
  {
    assert debugEnter(CLASS_NAME, "removeName",
                      String.valueOf(lowerName));
    names.remove(lowerName);
  }
  /**
   * Retrieves the path to the schema file that contains the
   * Retrieves the name of the schema file that contains the
   * definition for this DIT content rule.
   *
   * @return  The path to the schema file that contains the definition
   *          for this DIT content rule, or <CODE>null</CODE> if it is
   *          not known or if it is not stored in any schema file.
   * @return  The name of the schema file that contains the definition
   *          for this DIT content rule, or {@code null} if it is not
   *          known or if it is not stored in any schema file.
   */
  public String getSchemaFile()
  {
    assert debugEnter(CLASS_NAME, "getSchemaFile");
    return schemaFile;
    List<String> values =
         extraProperties.get(SCHEMA_PROPERTY_FILENAME);
    if ((values == null) || values.isEmpty())
    {
      return null;
    }
    return values.get(0);
  }
  /**
   * Specifies the path to the schema file that contains the
   * Specifies the name of the schema file that contains the
   * definition for this DIT content rule.
   *
   * @param  schemaFile  The path to the schema file that contains the
   * @param  schemaFile  The name of the schema file that contains the
   *                     definition for this DIT content rule.
   */
  public void setSchemaFile(String schemaFile)
@@ -332,7 +377,7 @@
    assert debugEnter(CLASS_NAME, "setSchemaFile",
                      String.valueOf(schemaFile));
    this.schemaFile = schemaFile;
    setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
  }
@@ -341,7 +386,7 @@
   * Retrieves the description for this DIT content rule.
   *
   * @return  The description for this DIT content rule, or
   *          <CODE>null</CODE> if there is none.
   *          {@code null} if there is none.
   */
  public String getDescription()
  {
@@ -353,28 +398,13 @@
  /**
   * Specifies the description for this DIT content rule.
   *
   * @param  description  The description for this DIT content rule.
   */
  public void setDescription(String description)
  {
    assert debugEnter(CLASS_NAME, "setDescription",
                      String.valueOf(description));
    this.description = description;
  }
  /**
   * Retrieves the set of auxiliary objectclasses that may be used for
   * entries associated with this DIT content rule.
   *
   * @return  The set of auxiliary objectclasses that may be used for
   *          entries associated with this DIT content rule.
   */
  public CopyOnWriteArraySet<ObjectClass> getAuxiliaryClasses()
  public Set<ObjectClass> getAuxiliaryClasses()
  {
    assert debugEnter(CLASS_NAME, "getAuxiliaryClasses");
@@ -384,68 +414,15 @@
  /**
   * Specifies the set of auxiliary objectclasses that may be used for
   * entries associated with this DIT content rule.
   *
   * @param  auxiliaryClasses  The set of auxiliary objectclasses that
   *                           may be used for entries associated with
   *                           this DIT content rule.
   */
  public void setAuxiliaryClasses(
                   CopyOnWriteArraySet<ObjectClass> auxiliaryClasses)
  {
    assert debugEnter(CLASS_NAME, "setAuxiliaryClasses",
                      String.valueOf(auxiliaryClasses));
    this.auxiliaryClasses = auxiliaryClasses;
  }
  /**
   * Adds the specified auxiliary objectclass to this DIT content
   * rule.
   *
   * @param  auxiliaryClass  The auxiliary class to add to this DIT
   *                         content rule.
   */
  public void addAuxiliaryClass(ObjectClass auxiliaryClass)
  {
    assert debugEnter(CLASS_NAME, "addAuxiliaryClass",
                      String.valueOf(auxiliaryClass));
    auxiliaryClasses.add(auxiliaryClass);
  }
  /**
   * Removes the specified auxiliary objectclass from this DIT content
   * rule.
   *
   * @param  auxiliaryClass  The auxiliary class to remove from this
   *                         DIT content rule.
   */
  public void removeAuxiliaryClass(ObjectClass auxiliaryClass)
  {
    assert debugEnter(CLASS_NAME, "removeAuxiliaryClass",
                      String.valueOf(auxiliaryClass));
    auxiliaryClasses.remove(auxiliaryClass);
  }
  /**
   * Indicates whether the provided auxiliary objectclass is allowed
   * for use by this DIT content rule.
   *
   * @param  auxiliaryClass  The auxiliary objectclass for which to
   *                         make the determination.
   *
   * @return  <CODE>true</CODE> if the provided auxiliary objectclass
   *          is allowed for use by this DIT content rule, or
   *          <CODE>false</CODE> if not.
   * @return  {@code true} if the provided auxiliary objectclass is
   *          allowed for use by this DIT content rule, or
   *          {@code false} if not.
   */
  public boolean isAllowedAuxiliaryClass(ObjectClass auxiliaryClass)
  {
@@ -463,7 +440,7 @@
   * @return  The set of required attributes for this DIT content
   *          rule.
   */
  public CopyOnWriteArraySet<AttributeType> getRequiredAttributes()
  public Set<AttributeType> getRequiredAttributes()
  {
    assert debugEnter(CLASS_NAME, "getRequiredAttributes");
@@ -479,9 +456,8 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          required by this DIT content rule, or <CODE>false</CODE>
   *          if not.
   * @return  {@code true} if the provided attribute type is required
   *          by this DIT content rule, or {@code false} if not.
   */
  public boolean isRequired(AttributeType attributeType)
  {
@@ -494,67 +470,13 @@
  /**
   * Specifies the set of required attributes for this DIT content
   * rule.
   *
   * @param  requiredAttributes  The set of required attributes for
   *                             this DIT content rule.
   */
  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 DIT content rule.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        required attributes for this DIT content
   *                        rule.
   */
  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 DIT content rule.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        of required attributes for this DIT
   *                        content rule.
   */
  public void removeRequiredAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "removeRequiredAttribute",
                      String.valueOf(attributeType));
    requiredAttributes.remove(attributeType);
  }
  /**
   * Retrieves the set of optional attributes for this DIT content
   * rule.
   *
   * @return  The set of optional attributes for this DIT content
   *          rule.
   */
  public CopyOnWriteArraySet<AttributeType> getOptionalAttributes()
  public Set<AttributeType> getOptionalAttributes()
  {
    assert debugEnter(CLASS_NAME, "getOptionalAttributes");
@@ -570,9 +492,8 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          optional for this DIT content rule, or
   *          <CODE>false</CODE> if not.
   * @return  {@code true} if the provided attribute type is optional
   *          for this DIT content rule, or {@code false} if not.
   */
  public boolean isOptional(AttributeType attributeType)
  {
@@ -585,69 +506,15 @@
  /**
   * Specifies the set of optional attributes for this DIT content
   * rule.
   *
   * @param  optionalAttributes  The set of optional attributes for
   *                             this DIT content rule.
   */
  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 DIT content rule.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        optional attributes for this DIT content
   *                        rule.
   */
  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 DIT content rule.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        of optional attributes for this DIT
   *                        content rule.
   */
  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 DIT content rule.
   *
   * @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 DIT content rule, or
   *          <CODE>false</CODE> if it is not.
   * @return  {@code true} if the provided attribute type is required
   *          or allowed for this DIT content rule, or {@code false}
   *          if it is not.
   */
  public boolean isRequiredOrOptional(AttributeType attributeType)
  {
@@ -672,9 +539,9 @@
   *                        allowed for an objectclass will be
   *                        acceptable.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          required or allowed for this DIT content rule, or
   *          <CODE>false</CODE> if it is not.
   * @return  {@code true} if the provided attribute type is required
   *          or allowed for this DIT content rule, or {@code false}
   *          if it is not.
   */
  public boolean isRequiredOrOptional(AttributeType attributeType,
                                      boolean acceptEmpty)
@@ -702,7 +569,7 @@
   * @return  The set of prohibited attributes for this DIT content
   *          rule.
   */
  public CopyOnWriteArraySet<AttributeType> getProhibitedAttributes()
  public Set<AttributeType> getProhibitedAttributes()
  {
    assert debugEnter(CLASS_NAME, "getProhibitedAttributes");
@@ -718,9 +585,9 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          prohibited for this DIT content rule, or
   *          <CODE>false</CODE> if not.
   * @return  {@code true} if the provided attribute type is
   *          prohibited for this DIT content rule, or {@code false}
   *          if not.
   */
  public boolean isProhibited(AttributeType attributeType)
  {
@@ -733,65 +600,10 @@
  /**
   * Specifies the set of prohibited attributes for this DIT content
   * rule.
   *
   * @param  prohibitedAttributes  The set of prohibited attributes
   *                               for this DIT content rule.
   */
  public void setProhibitedAttributes(
                   CopyOnWriteArraySet<AttributeType>
                       prohibitedAttributes)
  {
    assert debugEnter(CLASS_NAME, "setProhibitedAttributes",
                      String.valueOf(prohibitedAttributes));
    this.prohibitedAttributes = prohibitedAttributes;
  }
  /**
   * Adds the provided attribute to the set of prohibited attributes
   * for this DIT content rule.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        prohibited attributes for this DIT
   *                        content rule.
   */
  public void addProhibitedAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "addProhibitedAttribute",
                      String.valueOf(attributeType));
    prohibitedAttributes.add(attributeType);
  }
  /**
   * Removes the provided attribute from the set of prohibited
   * attributes for this DIT content rule.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        of prohibited attributes for this DIT
   *                        content rule.
   */
  public void removeProhibitedAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "removeProhibitedAttribute",
                      String.valueOf(attributeType));
    prohibitedAttributes.remove(attributeType);
  }
  /**
   * Indicates whether this DIT content rule is declared "obsolete".
   *
   * @return  <CODE>true</CODE> if this DIT content rule is declared
   *          "obsolete", or <CODE>false</CODE> if it is not.
   * @return  {@code true} if this DIT content rule is declared
   *          "obsolete", or {@code false} if it is not.
   */
  public boolean isObsolete()
  {
@@ -803,33 +615,15 @@
  /**
   * Specifies whether this DIT content rule is declared "obsolete".
   *
   * @param  isObsolete  Specifies whether this DIT content rule is
   *                     declared "obsolete".
   */
  public void setObsolete(boolean isObsolete)
  {
    assert debugEnter(CLASS_NAME, "setObsolete",
                      String.valueOf(isObsolete));
    this.isObsolete = isObsolete;
  }
  /**
   * Retrieves a mapping between the names of any extra non-standard
   * properties that may be associated with this DIT content rule and
   * the value for that property.  The caller may alter the contents
   * of this mapping.
   * the value for that property.
   *
   * @return  A mapping between the names of any extra non-standard
   *          properties that may be associated with this DIT content
   *          rule and the value for that property.
   */
  public ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
              getExtraProperties()
  public Map<String,List<String>> getExtraProperties()
  {
    assert debugEnter(CLASS_NAME, "getExtraProperties");
@@ -846,11 +640,10 @@
   *                       to retrieve the value.
   *
   * @return  The value of the specified "extra" property for this DIT
   *          content rule, or <CODE>null</CODE> if no such property
   *          is defined.
   *          content rule, or {@code null} if no such property is
   *          defined.
   */
  public CopyOnWriteArrayList<String>
              getExtraProperty(String propertyName)
  public List<String> getExtraProperty(String propertyName)
  {
    assert debugEnter(CLASS_NAME, "getExtraProperty",
                      String.valueOf(propertyName));
@@ -861,6 +654,66 @@
  /**
   * Specifies the provided "extra" property for this DIT content
   * rule.
   *
   * @param  name   The name for the "extra" property.  It must not be
   *                {@code null}.
   * @param  value  The value for the "extra" property, or
   *                {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, String value)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(value));
    ensureNotNull(name);
    if (value == null)
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> values = new LinkedList<String>();
      values.add(value);
      extraProperties.put(name, values);
    }
  }
  /**
   * Specifies the provided "extra" property for this DIT content
   * rule.
   *
   * @param  name    The name for the "extra" property.  It must not
   *                 be {@code null}.
   * @param  values  The set of value for the "extra" property, or
   *                 {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, List<String> values)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(values));
    ensureNotNull(name);
    if ((values == null) || values.isEmpty())
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> valuesCopy = new LinkedList<String>(values);
      extraProperties.put(name, valuesCopy);
    }
  }
  /**
   * Indicates whether the provided object is equal to this DIT
   * content rule.  The object will be considered equal if it is a DIT
   * content rule for the same structural objectclass and the same
@@ -871,8 +724,8 @@
   *
   * @param  o  The object for which to make the determination.
   *
   * @return  <CODE>true</CODE> if the provided object is equal to
   *          this DIT content rule, or <CODE>false</CODE> if not.
   * @return  {@code true} if the provided object is equal to
   *          this DIT content rule, or {@code false} if not.
   */
  public boolean equals(Object o)
  {
@@ -1112,8 +965,13 @@
    {
      for (String property : extraProperties.keySet())
      {
        CopyOnWriteArrayList<String> valueList =
             extraProperties.get(property);
        if ((! includeFileElement) &&
            property.equals(SCHEMA_PROPERTY_FILENAME))
        {
          continue;
        }
        List<String> valueList = extraProperties.get(property);
        buffer.append(" ");
        buffer.append(property);
@@ -1140,16 +998,6 @@
      }
    }
    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(" )");
  }
}
opendj-sdk/opends/src/server/org/opends/server/types/DITStructureRule.java
@@ -22,16 +22,21 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.schema.DITStructureRuleSyntax;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.loggers.Error.*;
@@ -39,6 +44,7 @@
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -46,7 +52,8 @@
 * This class defines a DIT structure rule, which is used to indicate
 * the types of children that entries may have.
 */
public class DITStructureRule
public final class DITStructureRule
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -57,38 +64,39 @@
  // Indicates whether this DIT structure rule is declared "obsolete".
  private boolean isObsolete;
  private final boolean isObsolete;
  // The rule ID for this DIT structure rule.
  private final int ruleID;
  // The name form for this DIT structure rule.
  private final NameForm nameForm;
  // The set of additional name-value pairs associated with this DIT
  // structure rule.
  private ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
               extraProperties;
  private final Map<String,List<String>> extraProperties;
  // The set of names for this DIT structure rule, in a mapping
  // between the all-lowercase form and the user-defined form.
  private ConcurrentHashMap<String,String> names;
  private final Map<String,String> names;
  // The set of superior DIT structure rules.
  private CopyOnWriteArraySet<DITStructureRule> superiorRules;
  private final Set<DITStructureRule> superiorRules;
  // The rule ID for this DIT structure rule.
  private int ruleID;
  // The name form for this DIT structure rule.
  private NameForm nameForm;
  // The definition string for this DIT structure rule.
  private final String definition;
  // The description for this DIT structure rule.
  private String description;
  // The path to the schema file that contains this DIT structure rule
  // definition.
  private String schemaFile;
  private final String description;
  /**
   * Creates a new DIT structure rule with the provided information.
   *
   * @param  definition       The definition string used to create
   *                          this DIT structure rule.  It must not be
   *                          {@code null}.
   * @param  names            The set of names for this DIT structure
   *                          rule, mapping the lowercase names to the
   *                          user-defined values.
@@ -104,33 +112,104 @@
   * @param  extraProperties  The set of "extra" properties associated
   *                          with this DIT structure rules.
   */
  public DITStructureRule(ConcurrentHashMap<String,String> names,
              int ruleID, String description,  boolean isObsolete,
              NameForm nameForm,
              CopyOnWriteArraySet<DITStructureRule> superiorRules,
              ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
                   extraProperties)
  public DITStructureRule(String definition, Map<String,String> names,
                          int ruleID, String description,
                          boolean isObsolete, NameForm nameForm,
                          Set<DITStructureRule> superiorRules,
                          Map<String,List<String>> extraProperties)
  {
    assert debugConstructor(CLASS_NAME,
                            new String[]
                            {
                              String.valueOf(names),
                              String.valueOf(ruleID),
                              String.valueOf(description),
                              String.valueOf(isObsolete),
                              String.valueOf(nameForm),
                              String.valueOf(superiorRules),
                              String.valueOf(extraProperties)
                            });
    assert debugConstructor(CLASS_NAME, String.valueOf(definition),
                            String.valueOf(names),
                            String.valueOf(ruleID),
                            String.valueOf(description),
                            String.valueOf(isObsolete),
                            String.valueOf(nameForm),
                            String.valueOf(superiorRules),
                            String.valueOf(extraProperties));
    this.names           = names;
    this.ruleID          = ruleID;
    this.description     = description;
    this.isObsolete      = isObsolete;
    this.nameForm        = nameForm;
    this.superiorRules   = superiorRules;
    this.schemaFile      = null;
    this.extraProperties = extraProperties;
    ensureNotNull(definition);
    this.definition  = definition;
    this.ruleID      = ruleID;
    this.description = description;
    this.isObsolete  = isObsolete;
    this.nameForm    = nameForm;
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
    }
    else
    {
      this.names = new LinkedHashMap<String,String>(names);
    }
    if ((superiorRules == null) || superiorRules.isEmpty())
    {
      this.superiorRules = new LinkedHashSet<DITStructureRule>(0);
    }
    else
    {
      this.superiorRules =
           new LinkedHashSet<DITStructureRule>(superiorRules);
    }
    if ((extraProperties == null) || extraProperties.isEmpty())
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(0);
    }
    else
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(extraProperties);
    }
  }
  /**
   * Retrieves the definition string used to create this DIT structure
   * rule.
   *
   * @return  The definition string used to create this DIT structure
   *          rule.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this DIT structure rule based on the
   * definition string.  It will also preserve other state information
   * associated with this DIT structure rule that is not included in
   * the definition string (e.g., the name of the schema file with
   * which it is associated).
   *
   * @return  The new instance of this DIT structure rule based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new DIT structure rule
   *                              instance from the definition string.
   */
  public DITStructureRule recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryConfig.getSchema();
    DITStructureRule dsr =
         DITStructureRuleSyntax.decodeDITStructureRule(value, schema,
                                                       false);
    dsr.setSchemaFile(getSchemaFile());
    return dsr;
  }
@@ -144,7 +223,7 @@
   * @return  The set of names that may be used to reference this DIT
   *          structure rule.
   */
  public ConcurrentHashMap<String,String> getNames()
  public Map<String,String> getNames()
  {
    assert debugEnter(CLASS_NAME, "getNames");
@@ -154,31 +233,13 @@
  /**
   * Specifies the set of names that may be used to reference this DIT
   * structure rule.  The provided set must contain a mapping between
   * each name in all lowercase characters and the name in a
   * user-defined form (which may include mixed capitalization).
   *
   * @param  names  The set of names that may be used to reference
   *                this attribute type.
   */
  public void setNames(ConcurrentHashMap<String,String> names)
  {
    assert debugEnter(CLASS_NAME, "setNames", String.valueOf(names));
    this.names = names;
  }
  /**
   * Indicates whether this DIT structure rule 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 DIT structure rule, or <CODE>false</CODE> if not.
   * @return  {@code true} if the specified name is assigned to this
   *          DIT structure rule, or {@code false} if not.
   */
  public boolean hasName(String lowerName)
  {
@@ -191,41 +252,6 @@
  /**
   * Adds the specified name to the set of names for this DIT
   * structure rule.
   *
   * @param  name  The name to add to the set of names for this DIT
   *               structure rule.
   */
  public void addName(String name)
  {
    assert debugEnter(CLASS_NAME, "addName", String.valueOf(name));
    String lowerName = toLowerCase(name);
    names.put(lowerName, name);
  }
  /**
   * Removes the specified name from the set of names for this DIT
   * structure rule.  This will have no effect if the specified name
   * is not associated with this attribute type.
   *
   * @param  lowerName  The lowercase name to remove from the set of
   *                    names for this DIT structure rule.
   */
  public void removeName(String lowerName)
  {
    assert debugEnter(CLASS_NAME, "removeName",
                      String.valueOf(lowerName));
    names.remove(lowerName);
  }
  /**
   * Retrieves the rule ID for this DIT structure rule.
   *
   * @return  The rule ID for this DIT structure rule.
@@ -240,21 +266,6 @@
  /**
   * Specifies the rule ID for this DIT structure rule.
   *
   * @param  ruleID  The rule ID for this DIT structure rule.
   */
  public void setRuleID(int ruleID)
  {
    assert debugEnter(CLASS_NAME, "setRuleID",
                      String.valueOf(ruleID));
    this.ruleID = ruleID;
  }
  /**
   * Retrieves the name or rule ID for this DIT structure rule.  If it
   * has one or more names, then the primary name will be returned.
   * If it does not have any names, then the rule ID will be returned.
@@ -278,44 +289,25 @@
  /**
   * Indicates whether this DIT structure rule has the specified name
   * or rule ID.
   *
   * @param  lowerValue  The lowercase value for which to make the
   *                     determination.
   *
   * @return  <CODE>true</CODE> if the provided value matches the rule
   *          ID or one of the names assigned to this DIT structure
   *          rule, or <CODE>false</CODE> if not.
   */
  public boolean hasNameOrOID(String lowerValue)
  {
    assert debugEnter(CLASS_NAME, "hasNameOrOID",
                      String.valueOf(lowerValue));
    if (names.containsKey(lowerValue))
    {
      return true;
    }
    return lowerValue.equals(String.valueOf(ruleID));
  }
  /**
   * Retrieves the path to the schema file that contains the
   * definition for this DIT structure rule.
   *
   * @return  The path to the schema file that contains the definition
   *          for this DIT structure rule, or <CODE>null</CODE> if it
   *          for this DIT structure rule, or {@code null} if it
   *          is not known or if it is not stored in any schema file.
   */
  public String getSchemaFile()
  {
    assert debugEnter(CLASS_NAME, "getSchemaFile");
    return schemaFile;
    List<String> values =
         extraProperties.get(SCHEMA_PROPERTY_FILENAME);
    if ((values == null) || values.isEmpty())
    {
      return null;
    }
    return values.get(0);
  }
@@ -332,7 +324,7 @@
    assert debugEnter(CLASS_NAME, "setSchemaFile",
                      String.valueOf(schemaFile));
    this.schemaFile = schemaFile;
    setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
  }
@@ -352,20 +344,6 @@
  /**
   * Specifies the description for this DIT structure rule.
   *
   * @param  description  The description for this DIT structure rule.
   */
  public void setDescription(String description)
  {
    assert debugEnter(CLASS_NAME, "setDescription", description);
    this.description = description;
  }
  /**
   * Retrieves the name form for this DIT structure rule.
   *
   * @return  The name form for this DIT structure rule.
@@ -380,21 +358,6 @@
  /**
   * Specifies the name form for this DIT structure rule.
   *
   * @param  nameForm  The name form for this DIT structure rule.
   */
  public void setNameForm(NameForm nameForm)
  {
    assert debugEnter(CLASS_NAME, "setNameForm",
                      String.valueOf(nameForm));
    this.nameForm = nameForm;
  }
  /**
   * Retrieves the structural objectclass for the name form with which
   * this DIT structure rule is associated.
   *
@@ -415,7 +378,7 @@
   *
   * @return  The set of superior rules for this DIT structure rule.
   */
  public CopyOnWriteArraySet<DITStructureRule> getSuperiorRules()
  public Set<DITStructureRule> getSuperiorRules()
  {
    assert debugEnter(CLASS_NAME, "getSuperiorRules");
@@ -428,8 +391,8 @@
   * Indicates whether this DIT structure rule has one or more
   * superior rules.
   *
   * @return  <CODE>true</CODE> if this DIT structure rule has one or
   *          more superior rules, or <CODE>false</CODE> if not.
   * @return  {@code true} if this DIT structure rule has one or more
   *          superior rules, or {@code false} if not.
   */
  public boolean hasSuperiorRules()
  {
@@ -441,62 +404,10 @@
  /**
   * Specifies the set of superior rules for this DIT structure rule.
   *
   * @param  superiorRules  The set of superior rules for this DIT
   *                        structure rule.
   */
  public void setSuperiorRules(CopyOnWriteArraySet<DITStructureRule>
                                    superiorRules)
  {
    assert debugEnter(CLASS_NAME, "setSuperiorRules",
                      String.valueOf(superiorRules));
    this.superiorRules = superiorRules;
  }
  /**
   * Adds the provided rule as a superior rule for this DIT structure
   * rule.
   *
   * @param  superiorRule  The superior rule to add to this DIT
   *                       structure rule.
   */
  public void addSuperiorRule(DITStructureRule superiorRule)
  {
    assert debugEnter(CLASS_NAME, "addSuperiorRule",
                      String.valueOf(superiorRule));
    superiorRules.add(superiorRule);
  }
  /**
   * Removes the provided rule as a superior rule for this DIT
   * structure rule.  It will have no effect if the provided rule is
   * not a superior rule for this DIT structure rule.
   *
   * @param  superiorRule  The superior rule to remove from this DIT
   *                       structure rule.
   */
  public void removeSuperiorRule(DITStructureRule superiorRule)
  {
    assert debugEnter(CLASS_NAME, "removeSuperiorRule",
                      String.valueOf(superiorRule));
    superiorRules.remove(superiorRule);
  }
  /**
   * Indicates whether this DIT structure rule is declared "obsolete".
   *
   * @return  <CODE>true</CODE> if this DIT structure rule is declared
   *          "obsolete", or <CODE>false</CODE> if not.
   * @return  {@code true} if this DIT structure rule is declared
   *          "obsolete", or {@code false} if not.
   */
  public boolean isObsolete()
  {
@@ -508,33 +419,15 @@
  /**
   * Specifies whether this DIT structure rule is declared "obsolete".
   *
   * @param  isObsolete  Specifies whether this DIT structure rule is
   *                     declared "obsolete".
   */
  public void setObsolete(boolean isObsolete)
  {
    assert debugEnter(CLASS_NAME, "setObsolete",
                      String.valueOf(isObsolete));
    this.isObsolete = isObsolete;
  }
  /**
   * Retrieves a mapping between the names of any extra non-standard
   * properties that may be associated with this DIT structure rule
   * and the value for that property.  The caller may alter the
   * contents of this mapping.
   * and the value for that property.
   *
   * @return  A mapping between the names of any extra non-standard
   *          properties that may be associated with this DIT
   *          structure rule and the value for that property.
   */
  public ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
              getExtraProperties()
  public Map<String,List<String>> getExtraProperties()
  {
    assert debugEnter(CLASS_NAME, "getExtraProperties");
@@ -551,11 +444,10 @@
   *                       to retrieve the value.
   *
   * @return  The value of the specified "extra" property for this DIT
   *          structure rule, or <CODE>null</CODE> if no such property
   *          is defined.
   *          structure rule, or {@code null} if no such property is
   *          defined.
   */
  public CopyOnWriteArrayList<String>
              getExtraProperty(String propertyName)
  public List<String> getExtraProperty(String propertyName)
  {
    assert debugEnter(CLASS_NAME, "getExtraProperty",
                      String.valueOf(propertyName));
@@ -566,14 +458,74 @@
  /**
   * Specifies the provided "extra" property for this DIT structure
   * rule.
   *
   * @param  name   The name for the "extra" property.  It must not be
   *                {@code null}.
   * @param  value  The value for the "extra" property, or
   *                {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, String value)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(value));
    ensureNotNull(name);
    if (value == null)
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> values = new LinkedList<String>();
      values.add(value);
      extraProperties.put(name, values);
    }
  }
  /**
   * Specifies the provided "extra" property for this DIT structure
   * rule.
   *
   * @param  name    The name for the "extra" property.  It must not
   *                 be {@code null}.
   * @param  values  The set of value for the "extra" property, or
   *                 {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, List<String> values)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(values));
    ensureNotNull(name);
    if ((values == null) || values.isEmpty())
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> valuesCopy = new LinkedList<String>(values);
      extraProperties.put(name, valuesCopy);
    }
  }
  /**
   * Indicates whether the provided object is equal to this DIT
   * structure rule.  The object will be considered equal if it is a
   * DIT structure rule 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, or <CODE>false</CODE> if not.
   * @return  {@code true} if the provided object is equal to this
   *          attribute, or {@code false} if not.
   */
  public boolean equals(Object o)
  {
@@ -718,8 +670,13 @@
    {
      for (String property : extraProperties.keySet())
      {
        CopyOnWriteArrayList<String> valueList =
             extraProperties.get(property);
        if ((! includeFileElement) &&
            property.equals(SCHEMA_PROPERTY_FILENAME))
        {
          continue;
        }
        List<String> valueList = extraProperties.get(property);
        buffer.append(" ");
        buffer.append(property);
@@ -746,16 +703,6 @@
      }
    }
    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(" )");
  }
}
opendj-sdk/opends/src/server/org/opends/server/types/MatchingRuleUse.java
@@ -22,22 +22,27 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.api.MatchingRule;
import org.opends.server.schema.MatchingRuleUseSyntax;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -47,7 +52,8 @@
 * the set of attribute types that may be used for a given matching
 * rule.
 */
public class MatchingRuleUse
public final class MatchingRuleUse
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -58,32 +64,30 @@
  // Indicates whether this matching rule use is declared "obsolete".
  private boolean isObsolete;
  private final boolean isObsolete;
  // The set of additional name-value pairs associated with this
  // matching rule use definition.
  private final Map<String,List<String>> extraProperties;
  // The set of names that may be used to refer to this matching rule
  // use, mapped between their all-lowercase representations and the
  // user-defined representations.
  private ConcurrentHashMap<String,String> names;
  // The set of attribute types with which this matching rule use is
  // associated.
  private CopyOnWriteArraySet<AttributeType> attributes;
  // The set of additional name-value pairs associated with this
  // matching rule use definition.
  private ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
               extraProperties;
  private final Map<String,String> names;
  // The matching rule with which this matching rule use is
  // associated.
  private MatchingRule matchingRule;
  private final MatchingRule matchingRule;
  // The set of attribute types with which this matching rule use is
  // associated.
  private final Set<AttributeType> attributes;
  // The definition string used to create this matching rule use.
  private final String definition;
  // The description for this matching rule use.
  private String description;
  // The path to the schema file that contains this matching rule use
  // definition.
  private String schemaFile;
  private final String description;
@@ -91,8 +95,11 @@
   * Creates a new matching rule use definition with the provided
   * information.
   *
   * @param  definition       The definition string used to create
   *                          this matching rule use.  It must not be
   *                          {@code null}.
   * @param  matchingRule     The matching rule for this matching rule
   *                          use.
   *                          use.  It must not be {@code null}.
   * @param  names            The set of names for this matching rule
   *                          use.
   * @param  description      The description for this matching rule
@@ -104,32 +111,100 @@
   * @param  extraProperties  A set of "extra" properties that may be
   *                          associated with this matching rule use.
   */
  public MatchingRuleUse(MatchingRule matchingRule,
              ConcurrentHashMap<String,String> names,
              String description, boolean isObsolete,
              CopyOnWriteArraySet<AttributeType> attributes,
              ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
                   extraProperties)
  public MatchingRuleUse(String definition, MatchingRule matchingRule,
                         Map<String,String> names, String description,
                         boolean isObsolete,
                         Set<AttributeType> attributes,
                         Map<String,List<String>> extraProperties)
  {
    assert debugConstructor(CLASS_NAME,
                            new String[]
                            {
                              String.valueOf(matchingRule),
                              String.valueOf(names),
                              String.valueOf(description),
                              String.valueOf(isObsolete),
                              String.valueOf(attributes),
                              String.valueOf(extraProperties)
                            });
    assert debugConstructor(CLASS_NAME, String.valueOf(definition),
                            String.valueOf(matchingRule),
                            String.valueOf(names),
                            String.valueOf(description),
                            String.valueOf(isObsolete),
                            String.valueOf(attributes),
                            String.valueOf(extraProperties));
    ensureNotNull(definition, matchingRule);
    this.definition   = definition;
    this.matchingRule = matchingRule;
    this.description  = description;
    this.isObsolete   = isObsolete;
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
    }
    else
    {
      this.names = new LinkedHashMap<String,String>(names);
    }
    if ((attributes == null) || attributes.isEmpty())
    {
      this.attributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.attributes = new LinkedHashSet<AttributeType>(attributes);
    }
    if ((extraProperties == null) || extraProperties.isEmpty())
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(0);
    }
    else
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(extraProperties);
    }
  }
    this.matchingRule    = matchingRule;
    this.names           = names;
    this.description     = description;
    this.isObsolete      = isObsolete;
    this.attributes      = attributes;
    this.schemaFile      = null;
    this.extraProperties = extraProperties;
  /**
   * Retrieves the definition string used to create this matching rule
   * use.
   *
   * @return  The definition string used to create this matching rule
   *          use.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this matching rule use based on the
   * definition string.  It will also preserve other state information
   * associated with this matching rule use that is not included in
   * the definition string (e.g., the name of the schema file with
   * which it is associated).
   *
   * @return  The new instance of this matching rule use based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new matching rule use
   *                              instance from the definition string.
   */
  public MatchingRuleUse recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryConfig.getSchema();
    MatchingRuleUse mru =
         MatchingRuleUseSyntax.decodeMatchingRuleUse(value, schema);
    mru.setSchemaFile(getSchemaFile());
    return mru;
  }
@@ -149,29 +224,13 @@
  /**
   * Specifies the matching rule for this matching rule use.
   *
   * @param  matchingRule  The matching rule for this matching rule
   *                       use.
   */
  public void setMatchingRule(MatchingRule matchingRule)
  {
    assert debugEnter(CLASS_NAME, "setMatchingRule",
                      String.valueOf(matchingRule));
    this.matchingRule = matchingRule;
  }
  /**
   * Retrieves the set of names for this matching rule use.  The
   * mapping will be between the names in all lowercase form and the
   * names in the user-defined form.
   *
   * @return  The set of names for this matching rule use.
   */
  public ConcurrentHashMap<String,String> getNames()
  public Map<String,String> getNames()
  {
    assert debugEnter(CLASS_NAME, "getNames");
@@ -185,7 +244,7 @@
   * rule use.
   *
   * @return  The primary name to use when referencing this matching
   *          rule use, or <CODE>null</CODE> if there is none.
   *          rule use, or {@code null} if there is none.
   */
  public String getName()
  {
@@ -209,8 +268,8 @@
   * @param  lowerName  The name for which to make the determination,
   *                    formatted in all lowercase characters.
   *
   * @return  <CODE>true</CODE> if this matching rule use has the
   *          specified name, or <CODE>false</CODE> if not.
   * @return  {@code true} if this matching rule use has the specified
   *          name, or {@code false} if not.
   */
  public boolean hasName(String lowerName)
  {
@@ -223,69 +282,25 @@
  /**
   * Specifies the set of names for this matching rule use as a
   * mapping between the names in all lowercase form and the names in
   * the user-defined form.
   *
   * @param  names  The set of names for this matching rule use.
   */
  public void setNames(ConcurrentHashMap<String,String> names)
  {
    assert debugEnter(CLASS_NAME, "setNames", String.valueOf(names));
    this.names = names;
  }
  /**
   * Adds the provided name to the set of names for this matching rule
   * use.
   *
   * @param  name  The name to add to the set of names for this
   *               matching rule use.
   */
  public void addName(String name)
  {
    assert debugEnter(CLASS_NAME, "addName", String.valueOf(name));
    names.put(toLowerCase(name), name);
  }
  /**
   * Removes the provided name from the set of names for this matching
   * rule use.  This will have no effect if the specified name is not
   * associated with this matching rule use.
   *
   * @param  lowerName  The name to remove from the set of names for
   *                    this matching rule use, in all lowercase
   *                    characters.
   */
  public void removeName(String lowerName)
  {
    assert debugEnter(CLASS_NAME, "removeName",
                      String.valueOf(lowerName));
    names.remove(lowerName);
  }
  /**
   * Retrieves the path to the schema file that contains the
   * definition for this matching rule use.
   *
   * @return  The path to the schema file that contains the definition
   *          for this matching rule use, or <CODE>null</CODE> if it
   *          is not known or if it is not stored in any schema file.
   *          for this matching rule use, or {@code null} if it is not
   *          known or if it is not stored in any schema file.
   */
  public String getSchemaFile()
  {
    assert debugEnter(CLASS_NAME, "getSchemaFile");
    return schemaFile;
    List<String> values =
         extraProperties.get(SCHEMA_PROPERTY_FILENAME);
    if ((values == null) || values.isEmpty())
    {
      return null;
    }
    return values.get(0);
  }
@@ -302,7 +317,7 @@
    assert debugEnter(CLASS_NAME, "setSchemaFile",
                      String.valueOf(schemaFile));
    this.schemaFile = schemaFile;
    setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
  }
@@ -311,7 +326,7 @@
   * Retrieves the description for this matching rule use.
   *
   * @return  The description for this matching rule use, or
   *          <CODE>null</CODE> if there is none.
   *          {@code null} if there is none.
   */
  public String getDescription()
  {
@@ -323,25 +338,10 @@
  /**
   * Specifies the description for this matching rule use.
   *
   * @param  description  The description for this matching rule use.
   */
  public void setDescription(String description)
  {
    assert debugEnter(CLASS_NAME, "setDescription",
                      String.valueOf(description));
    this.description = description;
  }
  /**
   * Indicates whether this matching rule use is declared "obsolete".
   *
   * @return  <CODE>true</CODE> if this matching rule use is declared
   *          "obsolete", or <CODE>false</CODE> if it is not.
   * @return  {@code true} if this matching rule use is declared
   *          "obsolete", or {@code false} if it is not.
   */
  public boolean isObsolete()
  {
@@ -353,29 +353,13 @@
  /**
   * Specifies whether this matching rule use is declared "obsolete".
   *
   * @param  isObsolete  Specifies whether this matching rule use is
   *                     declared "obsolete".
   */
  public void setObsolete(boolean isObsolete)
  {
    assert debugEnter(CLASS_NAME, "setObsolete",
                      String.valueOf(isObsolete));
    this.isObsolete = isObsolete;
  }
  /**
   * Retrieves the set of attributes associated with this matching
   * rule use.
   *
   * @return  The set of attributes associated with this matching
   *          rule use.
   */
  public CopyOnWriteArraySet<AttributeType> getAttributes()
  public Set<AttributeType> getAttributes()
  {
    assert debugEnter(CLASS_NAME, "getAttributes");
@@ -391,9 +375,9 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          referenced by this matching rule use, or
   *          <CODE>false</CODE> if it is not.
   * @return  {@code true} if the provided attribute type is
   *          referenced by this matching rule use, or {@code false}
   *          if it is not.
   */
  public boolean appliesToAttribute(AttributeType attributeType)
  {
@@ -406,71 +390,15 @@
  /**
   * Specifies the set of attributes for this matching rule use.
   *
   * @param  attributes  The set of attributes for this matching rule
   *                     use.
   */
  public void setAttributes(
                   CopyOnWriteArraySet<AttributeType> attributes)
  {
    assert debugEnter(CLASS_NAME, "setAttributes",
                      String.valueOf(attributes));
    this.attributes = attributes;
  }
  /**
   * Adds the provided attribute type to the set of attributes for
   * this matching rule use.  This will have no effect if the provided
   * attribute type is already associated with this matching rule use.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        attributes for this matching rule use.
   */
  public void addAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "addAttribute",
                      String.valueOf(attributeType));
    attributes.add(attributeType);
  }
  /**
   * Removes the provided attribute type from the set of attributes
   * for this matching rule use.  This will have no effect if the
   * provided attribute type is not associated with this matching rule
   * use.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        of attributes for this matching rule use.
   */
  public void removeAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "addAttribute",
                      String.valueOf(attributeType));
    attributes.remove(attributeType);
  }
  /**
   * Retrieves a mapping between the names of any extra non-standard
   * properties that may be associated with this matching rule use and
   * the value for that property.  The caller may alter the contents
   * of this mapping.
   * the value for that property.
   *
   * @return  A mapping between the names of any extra non-standard
   *          properties that may be associated with this matching
   *          rule use and the value for that property.
   */
  public ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
              getExtraProperties()
  public Map<String,List<String>> getExtraProperties()
  {
    assert debugEnter(CLASS_NAME, "getExtraProperties");
@@ -487,11 +415,10 @@
   *                       to retrieve the value.
   *
   * @return  The value of the specified "extra" property for this
   *          matching rule use, or <CODE>null</CODE> if no such
   *          property is defined.
   *          matching rule use, or {@code null} if no such property
   *          is defined.
   */
  public CopyOnWriteArrayList<String>
              getExtraProperty(String propertyName)
  public List<String> getExtraProperty(String propertyName)
  {
    assert debugEnter(CLASS_NAME, "getExtraProperty",
                      String.valueOf(propertyName));
@@ -502,14 +429,74 @@
  /**
   * Specifies the provided "extra" property for this matching rule
   * use.
   *
   * @param  name   The name for the "extra" property.  It must not be
   *                {@code null}.
   * @param  value  The value for the "extra" property, or
   *                {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, String value)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(value));
    ensureNotNull(name);
    if (value == null)
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> values = new LinkedList<String>();
      values.add(value);
      extraProperties.put(name, values);
    }
  }
  /**
   * Specifies the provided "extra" property for this matching rule
   * use.
   *
   * @param  name    The name for the "extra" property.  It must not
   *                 be {@code null}.
   * @param  values  The set of value for the "extra" property, or
   *                 {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, List<String> values)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(values));
    ensureNotNull(name);
    if ((values == null) || values.isEmpty())
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> valuesCopy = new LinkedList<String>(values);
      extraProperties.put(name, valuesCopy);
    }
  }
  /**
   * Indicates whether the provided object is equal to this matching
   * rule use.  The object will be considered equal if it is a
   * matching rule use with the same matching rule.
   *
   * @param  o  The object for which to make the determination.
   *
   * @return  <CODE>true</CODE> if the provided object is equal to
   *          this matching rule use, or <CODE>false</CODE> if not.
   * @return  {@code true} if the provided object is equal to this
   *          matching rule use, or {@code false} if not.
   */
  public boolean equals(Object o)
  {
@@ -647,8 +634,13 @@
    {
      for (String property : extraProperties.keySet())
      {
        CopyOnWriteArrayList<String> valueList =
             extraProperties.get(property);
        if ((! includeFileElement) &&
            property.equals(SCHEMA_PROPERTY_FILENAME))
        {
          continue;
        }
        List<String> valueList = extraProperties.get(property);
        buffer.append(" ");
        buffer.append(property);
@@ -675,16 +667,6 @@
      }
    }
    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(" )");
  }
}
opendj-sdk/opends/src/server/org/opends/server/types/NameForm.java
@@ -22,20 +22,26 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Iterator;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.opends.server.schema.NameFormSyntax;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.util.ServerConstants.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -45,7 +51,8 @@
 * and/or may be used in the RDN of an entry with a given structural
 * objectclass.
 */
public class NameForm
public final class NameForm
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
@@ -56,49 +63,52 @@
  // Indicates whether this name form is declared "obsolete".
  private boolean isObsolete;
  private final boolean isObsolete;
  // The set of additional name-value pairs associated with this name
  // form definition.
  private ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
               extraProperties;
  private final Map<String,List<String>> extraProperties;
  // The mapping between the lowercase names and the user-provided
  // names for this name form.
  private ConcurrentHashMap<String,String> names;
  // The set of optional attribute types for this name form.
  private CopyOnWriteArraySet<AttributeType> optionalAttributes;
  // The set of required attribute types for this name form.
  private CopyOnWriteArraySet<AttributeType> requiredAttributes;
  private final Map<String,String> names;
  // The reference to the structural objectclass for this name form.
  private ObjectClass structuralClass;
  private final ObjectClass structuralClass;
  // The set of optional attribute types for this name form.
  private final Set<AttributeType> optionalAttributes;
  // The set of required attribute types for this name form.
  private final Set<AttributeType> requiredAttributes;
  // The definition string used to create this name form.
  private final String definition;
  // The description for this name form.
  private String description;
  private final String description;
  // The OID for this name form.
  private String oid;
  // The path to the schema file that contains the definition for this
  // name form.
  private String schemaFile;
  private final String oid;
  /**
   * Creates a new name form definition with the provided information.
   *
   * @param  definition          The definition string used to create
   *                             this name form.  It must not be
   *                             {@code null}.
   * @param  names               The set of names that may be used to
   *                             reference this name form.
   * @param  oid                 The OID for this name form.
   * @param  oid                 The OID for this name form.  It must
   *                             not be {@code null}.
   * @param  description         The description for this name form.
   * @param  isObsolete          Indicates whether this name form is
   *                             declared "obsolete".
   * @param  structuralClass     The structural objectclass with which
   *                             this name form is associated.
   *                             this name form is associated.  It
   *                             must not be {@code null}.
   * @param  requiredAttributes  The set of required attribute types
   *                             for this name form.
   * @param  optionalAttributes  The set of optional attribute types
@@ -106,36 +116,111 @@
   * @param  extraProperties     A set of extra properties for this
   *                             name form.
   */
  public NameForm(ConcurrentHashMap<String,String> names, String oid,
              String description, boolean isObsolete,
              ObjectClass structuralClass,
              CopyOnWriteArraySet<AttributeType> requiredAttributes,
              CopyOnWriteArraySet<AttributeType> optionalAttributes,
              ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
                   extraProperties)
  public NameForm(String definition, Map<String,String> names,
                  String oid, String description, boolean isObsolete,
                  ObjectClass structuralClass,
                  Set<AttributeType> requiredAttributes,
                  Set<AttributeType> optionalAttributes,
                  Map<String,List<String>> extraProperties)
  {
    assert debugConstructor(CLASS_NAME,
                            new String[]
                            {
                              String.valueOf(names),
                              String.valueOf(oid),
                              String.valueOf(description),
                              String.valueOf(isObsolete),
                              String.valueOf(structuralClass),
                              String.valueOf(requiredAttributes),
                              String.valueOf(optionalAttributes),
                              String.valueOf(extraProperties)
                            });
    assert debugConstructor(CLASS_NAME, String.valueOf(definition),
                            String.valueOf(names),
                            String.valueOf(oid),
                            String.valueOf(description),
                            String.valueOf(isObsolete),
                            String.valueOf(structuralClass),
                            String.valueOf(requiredAttributes),
                            String.valueOf(optionalAttributes),
                            String.valueOf(extraProperties));
    this.names              = names;
    this.oid                = oid;
    this.description        = description;
    this.isObsolete         = isObsolete;
    this.structuralClass    = structuralClass;
    this.requiredAttributes = requiredAttributes;
    this.optionalAttributes = optionalAttributes;
    this.schemaFile         = null;
    this.extraProperties    = extraProperties;
    ensureNotNull(definition, oid, structuralClass);
    this.definition      = definition;
    this.oid             = oid;
    this.description     = description;
    this.isObsolete      = isObsolete;
    this.structuralClass = structuralClass;
    if ((names == null) || names.isEmpty())
    {
      this.names = new LinkedHashMap<String,String>(0);
    }
    else
    {
      this.names = new LinkedHashMap<String,String>(names);
    }
    if ((requiredAttributes == null) || requiredAttributes.isEmpty())
    {
      this.requiredAttributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.requiredAttributes =
           new LinkedHashSet<AttributeType>(requiredAttributes);
    }
    if ((optionalAttributes == null) || optionalAttributes.isEmpty())
    {
      this.optionalAttributes = new LinkedHashSet<AttributeType>(0);
    }
    else
    {
      this.optionalAttributes =
           new LinkedHashSet<AttributeType>(optionalAttributes);
    }
    if ((extraProperties == null) || extraProperties.isEmpty())
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(0);
    }
    else
    {
      this.extraProperties =
           new LinkedHashMap<String,List<String>>(extraProperties);
    }
  }
  /**
   * Retrieves the definition string used to create this name form.
   *
   * @return  The definition string used to create this name form.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this name form based on the definition
   * string.  It will also preserve other state information associated
   * with this name form that is not included in the definition string
   * (e.g., the name of the schema file with which it is associated).
   *
   * @return  The new instance of this name form based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new name form instance
   *                              from the definition string.
   */
  public NameForm recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryConfig.getSchema();
    NameForm nf = NameFormSyntax.decodeNameForm(value, schema);
    nf.setSchemaFile(getSchemaFile());
    return nf;
  }
@@ -149,7 +234,7 @@
   * @return  The set of names that may be used to reference this
   *          name form.
   */
  public ConcurrentHashMap<String,String> getNames()
  public Map<String,String> getNames()
  {
    assert debugEnter(CLASS_NAME, "getNames");
@@ -159,33 +244,14 @@
  /**
   * Specifies the set of names that may be used to reference this
   * name form.  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 name form.
   */
  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 name form.
   *
   * @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 name form, or <CODE>false</CODE>
   *          if not.
   * @return  {@code true} if the provided lowercase name may be used
   *          to reference this name form, or {@code false} if not.
   */
  public boolean hasName(String lowerName)
  {
@@ -198,41 +264,6 @@
  /**
   * Adds the provided name to the set of names that may be used to
   * reference this name form.
   *
   * @param  name  The name to add to the set of names that may be
   *               used to reference this name form.
   */
  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 name form.
   *
   * @param  lowerName  The name to remove from the set of names that
   *                    may be used to reference this name form, in
   *                    all lowercase characters.
   */
  public void removeName(String lowerName)
  {
    assert debugEnter(CLASS_NAME, "removeName",
                      String.valueOf(lowerName));
    names.remove(lowerName);
  }
  /**
   * Retrieves the OID for this name form.
   *
   * @return  The OID for this name form.
@@ -247,20 +278,6 @@
  /**
   * Specifies the OID for this name form.
   *
   * @param  oid  The OID for this name form.
   */
  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
   * name form.  If at least one name is defined, then the first will
   * be returned.  Otherwise, the OID will be returned.
@@ -292,9 +309,9 @@
   * @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 name form, or
   *          <CODE>false</CODE> if it is not.
   * @return  {@code true} if the provided lowercase value is one of
   *          the names or the OID of this name form, or {@code false}
   *          if it is not.
   */
  public boolean hasNameOrOID(String lowerValue)
  {
@@ -316,14 +333,21 @@
   * definition for this name form.
   *
   * @return  The path to the schema file that contains the definition
   *          for this name form, or <CODE>null</CODE> if it is not
   *          known or if it is not stored in any schema file.
   *          for this name form, or {@code null} if it is not known
   *          or if it is not stored in any schema file.
   */
  public String getSchemaFile()
  {
    assert debugEnter(CLASS_NAME, "getSchemaFile");
    return schemaFile;
    List<String> values =
         extraProperties.get(SCHEMA_PROPERTY_FILENAME);
    if ((values == null) || values.isEmpty())
    {
      return null;
    }
    return values.get(0);
  }
@@ -340,7 +364,7 @@
    assert debugEnter(CLASS_NAME, "setSchemaFile",
                      String.valueOf(schemaFile));
    this.schemaFile = schemaFile;
    setExtraProperty(SCHEMA_PROPERTY_FILENAME, schemaFile);
  }
@@ -348,8 +372,8 @@
  /**
   * Retrieves the description for this name form.
   *
   * @return  The description for this name form, or <CODE>null</CODE>
   *          if there is none.
   * @return  The description for this name form, or {@code true} if
   *          there is none.
   */
  public String getDescription()
  {
@@ -361,21 +385,6 @@
  /**
   * Specifies the description for this name form.
   *
   * @param  description  The description for this name form.
   */
  public void setDescription(String description)
  {
    assert debugEnter(CLASS_NAME, "setDescription",
                      String.valueOf(description));
    this.description = description;
  }
  /**
   * Retrieves the reference to the structural objectclass for this
   * name form.
   *
@@ -392,27 +401,11 @@
  /**
   * Specifies the structural objectclass for this name form.
   *
   * @param  structuralClass  The structural objectclass for this name
   *                          form.
   */
  public void setStructuralClass(ObjectClass structuralClass)
  {
    assert debugEnter(CLASS_NAME, "setStructuralClass",
                      String.valueOf(structuralClass));
    this.structuralClass = structuralClass;
  }
  /**
   * Retrieves the set of required attributes for this name form.
   *
   * @return  The set of required attributes for this name form.
   */
  public CopyOnWriteArraySet<AttributeType> getRequiredAttributes()
  public Set<AttributeType> getRequiredAttributes()
  {
    assert debugEnter(CLASS_NAME, "getRequiredAttributes");
@@ -428,9 +421,8 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          required by this name form, or <CODE>false</CODE> if
   *          not.
   * @return  {@code true} if the provided attribute type is required
   *          by this name form, or {@code false} if not.
   */
  public boolean isRequired(AttributeType attributeType)
  {
@@ -443,62 +435,11 @@
  /**
   * Specifies the set of required attributes for this name form.
   *
   * @param  requiredAttributes  The set of required attributes for
   *                             this name form.
   */
  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 name form.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        required attributes for this name form.
   */
  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 name form.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        of required attributes for this name form.
   */
  public void removeRequiredAttribute(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "removeRequiredAttribute",
                      String.valueOf(attributeType));
    requiredAttributes.remove(attributeType);
  }
  /**
   * Retrieves the set of optional attributes for this name form.
   *
   * @return  The set of optional attributes for this name form.
   */
  public CopyOnWriteArraySet<AttributeType> getOptionalAttributes()
  public Set<AttributeType> getOptionalAttributes()
  {
    assert debugEnter(CLASS_NAME, "getOptionalAttributes");
@@ -514,9 +455,8 @@
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the provided attribute type is
   *          optional for this name form, or <CODE>false</CODE> if
   *          not.
   * @return  {@code true} if the provided attribute type is optional
   *          for this name form, or {@code false} if not.
   */
  public boolean isOptional(AttributeType attributeType)
  {
@@ -529,66 +469,15 @@
  /**
   * Specifies the set of optional attributes for this name form.
   *
   * @param  optionalAttributes  The set of optional attributes for
   *                             this name form.
   */
  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 name form.
   *
   * @param  attributeType  The attribute type to add to the set of
   *                        optional attributes for this name form.
   */
  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 name form.
   *
   * @param  attributeType  The attribute type to remove from the set
   *                        optional attributes for this name form.
   */
  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 name form.
   *
   * @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 name form, or
   *          <CODE>false</CODE> if it is not.
   * @return  {@code true} if the provided attribute type is required
   *          or optional for this name form, or {@code false} if it
   *          is not.
   */
  public boolean isRequiredOrOptional(AttributeType attributeType)
  {
@@ -604,8 +493,8 @@
  /**
   * Indicates whether this name form is declared "obsolete".
   *
   * @return  <CODE>true</CODE> if this name form is declared
   *          "obsolete", or <CODE>false</CODE> if it is not.
   * @return  {@code true} if this name form is declared
   *          "obsolete", or {@code false} if it is not.
   */
  public boolean isObsolete()
  {
@@ -617,33 +506,15 @@
  /**
   * Specifies whether this name form is declared "obsolete".
   *
   * @param  isObsolete  Specifies whether this name form is declared
   *                     "obsolete".
   */
  public void setObsolete(boolean isObsolete)
  {
    assert debugEnter(CLASS_NAME, "setObsolete",
                      String.valueOf(isObsolete));
    this.isObsolete = isObsolete;
  }
  /**
   * Retrieves a mapping between the names of any extra non-standard
   * properties that may be associated with this name form and the
   * value for that property.  The caller may alter the contents of
   * this mapping.
   * value for that property.
   *
   * @return  A mapping between the names of any extra non-standard
   *          properties that may be associated with this name form
   *          and the value for that property.
   */
  public ConcurrentHashMap<String,CopyOnWriteArrayList<String>>
              getExtraProperties()
  public Map<String,List<String>> getExtraProperties()
  {
    assert debugEnter(CLASS_NAME, "getExtraProperties");
@@ -660,11 +531,10 @@
   *                       to retrieve the value.
   *
   * @return  The value of the specified "extra" property for this
   *          name form, or <CODE>null</CODE> if no such property is
   *          name form, or {@code null} if no such property is
   *          defined.
   */
  public CopyOnWriteArrayList<String>
              getExtraProperty(String propertyName)
  public List<String> getExtraProperty(String propertyName)
  {
    assert debugEnter(CLASS_NAME, "getExtraProperty",
                      String.valueOf(propertyName));
@@ -675,14 +545,72 @@
  /**
   * Specifies the provided "extra" property for this name form.
   *
   * @param  name   The name for the "extra" property.  It must not be
   *                {@code null}.
   * @param  value  The value for the "extra" property, or
   *                {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, String value)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(value));
    ensureNotNull(name);
    if (value == null)
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> values = new LinkedList<String>();
      values.add(value);
      extraProperties.put(name, values);
    }
  }
  /**
   * Specifies the provided "extra" property for this name form.
   *
   * @param  name    The name for the "extra" property.  It must not
   *                 be {@code null}.
   * @param  values  The set of value for the "extra" property, or
   *                 {@code null} if the property is to be removed.
   */
  public void setExtraProperty(String name, List<String> values)
  {
    assert debugEnter(CLASS_NAME, "setExtraProperty",
                      String.valueOf(name), String.valueOf(values));
    ensureNotNull(name);
    if ((values == null) || values.isEmpty())
    {
      extraProperties.remove(name);
    }
    else
    {
      LinkedList<String> valuesCopy = new LinkedList<String>(values);
      extraProperties.put(name, valuesCopy);
    }
  }
  /**
   * Indicates whether the provided object is equal to this name form.
   * The object will be considered equal if it is a name form with the
   * same OID as the current name form.
   *
   * @param  o  The object for which to make the determination.
   *
   * @return  <CODE>true</CODE> if the provided object is equal to
   *          this name form, or <CODE>false</CODE> if not.
   * @return  {@code true} if the provided object is equal to this
   *          name form, or {@code true} if not.
   */
  public boolean equals(Object o)
  {
@@ -861,8 +789,13 @@
    {
      for (String property : extraProperties.keySet())
      {
        CopyOnWriteArrayList<String> valueList =
             extraProperties.get(property);
        if ((! includeFileElement) &&
            property.equals(SCHEMA_PROPERTY_FILENAME))
        {
          continue;
        }
        List<String> valueList = extraProperties.get(property);
        buffer.append(" ");
        buffer.append(property);
@@ -889,16 +822,6 @@
      }
    }
    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(" )");
  }
}
opendj-sdk/opends/src/server/org/opends/server/types/ObjectClass.java
@@ -22,16 +22,12 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 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 java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
@@ -41,6 +37,13 @@
import java.util.Map;
import java.util.Set;
import org.opends.server.schema.ObjectClassSyntax;
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.Validator.*;
/**
@@ -58,7 +61,10 @@
 * fields are accessed via their getters or via the
 * {@link #toString()} methods.
 */
public final class ObjectClass extends CommonSchemaElements {
public final class ObjectClass
       extends CommonSchemaElements
       implements SchemaFileElement
{
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
@@ -89,6 +95,9 @@
  // contain any attribute.
  private final boolean isExtensibleObject;
  // The definition string used to create this objectclass.
  private final String definition;
  /**
@@ -100,62 +109,65 @@
   * from the set of <code>names</code> will be used as the primary
   * name.
   *
   * @param definition
   *          The definition string used to create this objectclass.
   *          It must not be {@code null}.
   * @param primaryName
   *          The primary name for this objectclass, or
   *          <code>null</code> if there is no primary name.
   *          {@code null} 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.
   *          objectclass.
   * @param oid
   *          The OID for this objectclass (must not be
   *          <code>null</code>).
   *          The OID for this objectclass.  It must not be
   *          {@code null}.
   * @param description
   *          The description for this objectclass, or
   *          <code>null</code> if there is no description.
   *          The description for this objectclass, or {@code null} if
   *          there is no description.
   * @param superiorClass
   *          The superior class for this objectclass, or
   *          <code>null</code> if there is no superior object
   *          classes.
   *          The superior class for this objectclass, or {@code null}
   *          if there is no superior object class.
   * @param requiredAttributes
   *          The set of required attribute types for this
   *          objectclass, or <code>null</code> if there are no
   *          required attribute types.
   *          objectclass.
   * @param optionalAttributes
   *          The set of optional attribute types for this
   *          objectclass, or <code>null</code> if there are no
   *          optional attribute types.
   *          objectclass.
   * @param objectClassType
   *          The objectclass type for this objectclass, or
   *          <code>null</code> to default to structural.
   *          {@code null} 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>.
   *          A set of extra properties for this objectclass.
   */
  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 {
  public ObjectClass(String definition, 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)
  {
    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));
                            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));
    ensureNotNull(definition, oid);
    this.definition    = definition;
    this.superiorClass = superiorClass;
    // Set flag indicating whether or not this object class allows any
@@ -214,6 +226,49 @@
  /**
   * Retrieves the definition string used to create this objectclass.
   *
   * @return  The definition string used to create this objectclass.
   */
  public String getDefinition()
  {
    assert debugEnter(CLASS_NAME, "getDefinition");
    return definition;
  }
  /**
   * Creates a new instance of this objectclass based on the
   * definition string.  It will also preserve other state information
   * associated with this objectclass that is not included in the
   * definition string (e.g., the name of the schema file with which
   * it is associated).
   *
   * @return  The new instance of this objectclass based on the
   *          definition string.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create a new objectclass instance
   *                              from the definition string.
   */
  public ObjectClass recreateFromDefinition()
         throws DirectoryException
  {
    ByteString value  = ByteStringFactory.create(definition);
    Schema     schema = DirectoryConfig.getSchema();
    ObjectClass oc = ObjectClassSyntax.decodeObjectClass(value,
                                                         schema);
    oc.setSchemaFile(getSchemaFile());
    return oc;
  }
  /**
   * Retrieves the reference to the superior class for this
   * objectclass.
   *
opendj-sdk/opends/src/server/org/opends/server/types/Schema.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
@@ -364,7 +364,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = attributeType.toString();
      String valueString = attributeType.getDefinition();
      ASN1OctetString rawValue = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -401,7 +401,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = attributeType.toString();
      String valueString = attributeType.getDefinition();
      ASN1OctetString rawValue = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -556,7 +556,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = objectClass.toString();
      String valueString = objectClass.getDefinition();
      ASN1OctetString rawValue = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -592,7 +592,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = objectClass.toString();
      String valueString = objectClass.getDefinition();
      ASN1OctetString rawValue = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -1821,7 +1821,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = matchingRuleUse.toString();
      String valueString = matchingRuleUse.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -1853,7 +1853,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = matchingRuleUse.toString();
      String valueString = matchingRuleUse.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -1989,7 +1989,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = ditContentRule.toString();
      String valueString = ditContentRule.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2021,7 +2021,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = ditContentRule.toString();
      String valueString = ditContentRule.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2234,7 +2234,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = ditStructureRule.toString();
      String valueString = ditStructureRule.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2269,7 +2269,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = ditStructureRule.toString();
      String valueString = ditStructureRule.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2496,7 +2496,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = nameForm.toString();
      String valueString = nameForm.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2531,7 +2531,7 @@
      // rather than the attribute type because otherwise it would use
      // a very expensive matching rule (OID first component match)
      // that would kill performance.
      String valueString = nameForm.toString();
      String valueString = nameForm.getDefinition();
      ASN1OctetString rawValue  = new ASN1OctetString(valueString);
      ASN1OctetString normValue =
           new ASN1OctetString(toLowerCase(valueString));
@@ -2542,6 +2542,244 @@
  /**
   * Recursively rebuilds all schema elements that are dependent upon
   * the provided element.  This must be invoked whenever an existing
   * schema element is modified in order to ensure that any elements
   * that depend on it should also be recreated to reflect the change.
   * <BR><BR>
   * The following conditions create dependencies between schema
   * elements:
   * <UL>
   *   <LI>If an attribute type references a superior attribute type,
   *       then it is dependent upon that superior attribute
   *       type.</LI>
   *   <LI>If an objectclass requires or allows an attribute type,
   *       then it is dependent upon that attribute type.</LI>
   *   <LI>If a name form requires or allows an attribute type in the
   *       RDN, then it is dependent upon that attribute type.</LI>
   *   <LI>If a DIT content rule requires, allows, or forbids the use
   *       of an attribute type, then it is dependent upon that
   *       attribute type.</LI>
   *   <LI>If a matching rule use references an attribute type, then
   *       it is dependent upon that attribute type.</LI>
   *   <LI>If an objectclass references a superior objectclass, then
   *       it is dependent upon that superior objectclass.</LI>
   *   <LI>If a name form references a structural objectclass, then it
   *       is dependent upon that objectclass.</LI>
   *   <LI>If a DIT content rule references a structural or auxiliary
   *       objectclass, then it is dependent upon that
   *       objectclass.</LI>
   *   <LI>If a DIT structure rule references a name form, then it is
   *       dependent upon that name form.</LI>
   *   <LI>If a DIT structure rule references a superior DIT structure
   *       rule, then it is dependent upon that superior DIT structure
   *       rule.</LI>
   * </UL>
   *
   * @param  element  The element for which to recursively rebuild all
   *                  dependent elements.
   *
   * @throws  DirectoryException  If a problem occurs while rebuilding
   *                              any of the schema elements.
   */
  public final void rebuildDependentElements(
                         SchemaFileElement element)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "rebuildDependentElements",
                      String.valueOf(element));
    try
    {
      rebuildDependentElements(element, 0);
    }
    catch (DirectoryException de)
    {
      // If we got an error as a result of a circular reference, then
      // we want to make sure that the schema element we call out is
      // the one that is at the root of the problem.
      if (de.getErrorMessageID() ==
          MSGID_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE)
      {
        int    msgID   = MSGID_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE;
        String message = getMessage(msgID, element.getDefinition());
        throw new DirectoryException(de.getResultCode(), message,
                                     msgID, de);
      }
      // It wasn't a circular reference error, so just re-throw the
      // exception.
      throw de;
    }
  }
  /**
   * Recursively rebuilds all schema elements that are dependent upon
   * the provided element, increasing the depth for each level of
   * recursion to protect against errors due to circular references.
   *
   * @param  element  The element for which to recursively rebuild all
   *                  dependent elements.
   * @param  depth    The current recursion depth.
   *
   * @throws  DirectoryException  If a problem occurs while rebuilding
   *                              any of the schema elements.
   */
  private final void rebuildDependentElements(
                          SchemaFileElement element, int depth)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "rebuildDependentElements",
                      String.valueOf(element), String.valueOf(depth));
    if (depth > 20)
    {
      // FIXME -- Is this an appropriate maximum depth for detecting
      // circular references?
      int    msgID   = MSGID_SCHEMA_CIRCULAR_DEPENDENCY_REFERENCE;
      String message = getMessage(msgID, element.getDefinition());
      throw new DirectoryException(ResultCode.UNWILLING_TO_PERFORM,
                                   message, msgID);
    }
    // Figure out what type of element we're dealing with and make the
    // appropriate determinations for that element.
    if (element instanceof AttributeType)
    {
      AttributeType t = (AttributeType) element;
      for (AttributeType at : attributeTypes.values())
      {
        if ((at.getSuperiorType() != null) &&
            at.getSuperiorType().equals(t))
        {
          AttributeType newAT = at.recreateFromDefinition();
          deregisterAttributeType(at);
          registerAttributeType(newAT, true);
          rebuildDependentElements(at, depth+1);
        }
      }
      for (ObjectClass oc : objectClasses.values())
      {
        if (oc.getRequiredAttributes().contains(t) ||
            oc.getOptionalAttributes().contains(t))
        {
          ObjectClass newOC = oc.recreateFromDefinition();
          deregisterObjectClass(oc);
          registerObjectClass(newOC, true);
          rebuildDependentElements(oc, depth+1);
        }
      }
      for (NameForm nf : nameFormsByOC.values())
      {
        if (nf.getRequiredAttributes().contains(t) ||
            nf.getOptionalAttributes().contains(t))
        {
          NameForm newNF = nf.recreateFromDefinition();
          deregisterNameForm(nf);
          registerNameForm(newNF, true);
          rebuildDependentElements(nf, depth+1);
        }
      }
      for (DITContentRule dcr : ditContentRules.values())
      {
        if (dcr.getRequiredAttributes().contains(t) ||
            dcr.getOptionalAttributes().contains(t) ||
            dcr.getProhibitedAttributes().contains(t))
        {
          DITContentRule newDCR = dcr.recreateFromDefinition();
          deregisterDITContentRule(dcr);
          registerDITContentRule(newDCR, true);
          rebuildDependentElements(dcr, depth+1);
        }
      }
      for (MatchingRuleUse mru : matchingRuleUses.values())
      {
        if (mru.getAttributes().contains(t))
        {
          MatchingRuleUse newMRU = mru.recreateFromDefinition();
          deregisterMatchingRuleUse(mru);
          registerMatchingRuleUse(newMRU, true);
          rebuildDependentElements(mru, depth+1);
        }
      }
    }
    else if (element instanceof ObjectClass)
    {
      ObjectClass c = (ObjectClass) element;
      for (ObjectClass oc : objectClasses.values())
      {
        if ((oc.getSuperiorClass() != null) &&
            oc.getSuperiorClass().equals(c))
        {
          ObjectClass newOC = oc.recreateFromDefinition();
          deregisterObjectClass(oc);
          registerObjectClass(newOC, true);
          rebuildDependentElements(oc, depth+1);
        }
      }
      NameForm nf = nameFormsByOC.get(c);
      if (nf != null)
      {
        NameForm newNF = nf.recreateFromDefinition();
        deregisterNameForm(nf);
        registerNameForm(newNF, true);
        rebuildDependentElements(nf, depth+1);
      }
      for (DITContentRule dcr : ditContentRules.values())
      {
        if (dcr.getStructuralClass().equals(c) ||
            dcr.getAuxiliaryClasses().contains(c))
        {
          DITContentRule newDCR = dcr.recreateFromDefinition();
          deregisterDITContentRule(dcr);
          registerDITContentRule(newDCR, true);
          rebuildDependentElements(dcr, depth+1);
        }
      }
    }
    else if (element instanceof NameForm)
    {
      NameForm n = (NameForm) element;
      DITStructureRule dsr = ditStructureRulesByNameForm.get(n);
      if (dsr != null)
      {
        DITStructureRule newDSR = dsr.recreateFromDefinition();
        deregisterDITStructureRule(dsr);
        registerDITStructureRule(newDSR, true);
        rebuildDependentElements(dsr, depth+1);
      }
    }
    else if (element instanceof DITStructureRule)
    {
      DITStructureRule d = (DITStructureRule) element;
      for (DITStructureRule dsr : ditStructureRulesByID.values())
      {
        if (dsr.getSuperiorRules().contains(d))
        {
          DITStructureRule newDSR = dsr.recreateFromDefinition();
          deregisterDITStructureRule(dsr);
          registerDITStructureRule(newDSR, true);
          rebuildDependentElements(dsr, depth+1);
        }
      }
    }
  }
  /**
   * Creates a new <CODE>Schema</CODE> object that is a duplicate of
   * this one.  It elements may be added and removed from the
   * duplicate without impacting this version.
opendj-sdk/opends/src/server/org/opends/server/types/SchemaFileElement.java
New file
@@ -0,0 +1,107 @@
/*
 * 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 2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
/**
 * This interface defines a set of methods that must be provided by a
 * schema file element, which is a schema element that is loaded from
 * a schema configuration file.
 * <BR><BR>
 * Note that this interface is not meant to be implemented by
 * third-party code, and only the following classes should be
 * considered schema file elements:
 * <UL>
 *   <LI>{@code org.opends.server.types.AttributeType}</LI>
 *   <LI>{@code org.opends.server.types.ObjectClass}</LI>
 *   <LI>{@code org.opends.server.types.NameForm}</LI>
 *   <LI>{@code org.opends.server.types.DITContentRule}</LI>
 *   <LI>{@code org.opends.server.types.DITStructureRule}</LI>
 *   <LI>{@code org.opends.server.types.MatchingRuleUse}</LI>
 * </UL>
 */
public interface SchemaFileElement
{
  /**
   * Retrieves the name of the schema file in which this element is
   * defined.
   *
   * @return  The name of the schema file in which this element is
   *          defined, or {@code null} if it is not known or this
   *          element is not defined in any schema file.
   */
  public String getSchemaFile();
  /**
   * Specifies the name of the schema file in which this element is
   * defined.
   *
   * @param  schemaFile  The name of the schema file in which this
   *                     element is defined, or {@code null} if it is
   *                     not defined in any schema file.
   */
  public void setSchemaFile(String schemaFile);
  /**
   * Retrieves the definition string that is used to represent this
   * element in the schema configuration file.
   *
   * @return  The definition string that should be used to represent
   *          this element in the schema configuration file.
   */
  public String getDefinition();
  /**
   * Creates a new instance of this schema element based on the
   * definition from the schema file.  The new instance should also
   * be created with all appropriate state information that may not
   * be directly represented in the schema definition (e.g., the name
   * of the schema file containing the definition).
   * <BR><BR>
   * Whenever an existing schema file element is modified with the
   * server online, this method will be invoked to recreate any
   * schema elements that might have been dependent upon the
   * modified element.
   *
   * @return  A new instance of this schema element based on the
   *          definition.
   *
   * @throws  DirectoryException  If a problem occurs while attempting
   *                              to create the new instance of this
   *                              schema element.
   */
  public SchemaFileElement recreateFromDefinition()
         throws DirectoryException;
}
opendj-sdk/opends/src/server/org/opends/server/util/ServerConstants.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.util;
@@ -1396,6 +1396,55 @@
  /**
   * The description for the alert type that will be used for the alert
   * notification generated if a problem occurs while creating copies of the
   * existing schema configuration files and a problem occurs that leaves the
   * schema configuration in a potentially inconsistent state.
   */
  public static final String ALERT_DESCRIPTION_CANNOT_COPY_SCHEMA_FILES =
      "This alert type will be used to notify administrators if a problem " +
      "occurs while attempting to create copies of the existing schema " +
      "configuration files before making a schema update, and the schema " +
      "configuration is left in a potentially inconsistent state.";
  /**
   * The alert type string that will be used for the alert notification
   * generated if a problem occurs while creating copies of the existing schema
   * files in a manner that may leave the schema configuration inconsistent.
   */
  public static final String ALERT_TYPE_CANNOT_COPY_SCHEMA_FILES =
       "org.opends.server.CannotCopySchemaFiles";
  /**
   * The description for the alert type that will be used for the alert
   * notification generated if a problem occurs while writing new versions of
   * the server schema configuration files and a problem occurs that leaves the
   * schema configuration in a potentially inconsistent state.
   */
  public static final String ALERT_DESCRIPTION_CANNOT_WRITE_NEW_SCHEMA_FILES =
      "This alert type will be used to notify administrators if a problem " +
      "occurs while attempting to write new verisons of the server schema " +
      "configuration files, and the schema configuration is left in a " +
      "potentially inconsistent state.";
  /**
   * The alert type string that will be used for the alert notification
   * generated if a problem occurs while writing new versions of the server
   * schema files in a manner that may leave the schema configuration
   * inconsistent.
   */
  public static final String ALERT_TYPE_CANNOT_WRITE_NEW_SCHEMA_FILES =
       "org.opends.server.CannotWriteNewSchemaFiles";
  /**
   * The name of the default password storage scheme that will be used for new
   * passwords.
   */
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaBackendTestCase.java
@@ -22,12 +22,14 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.backends;
import java.io.File;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
@@ -36,11 +38,14 @@
import org.testng.annotations.Test;
import org.opends.server.TestCaseUtils;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.core.AddOperation;
import org.opends.server.core.DeleteOperation;
import org.opends.server.core.DirectoryServer;
import org.opends.server.core.ModifyOperation;
import org.opends.server.core.ModifyDNOperation;
import org.opends.server.core.SchemaConfigManager;
import org.opends.server.protocols.internal.InternalClientConnection;
import org.opends.server.protocols.internal.InternalSearchOperation;
import org.opends.server.tools.LDAPModify;
@@ -49,10 +54,17 @@
import org.opends.server.types.AttributeValue;
import org.opends.server.types.ByteStringFactory;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DITContentRule;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.InitializationException;
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.MatchingRuleUse;
import org.opends.server.types.Modification;
import org.opends.server.types.ModificationType;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
@@ -94,6 +106,23 @@
  /**
   * Tests the {@code initializeBackend} method by providing a null
   * configuration entry.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(expectedExceptions = { ConfigException.class,
                               InitializationException.class })
  public void testInitializeWithNullEntry()
         throws Exception
  {
    SchemaBackend schemaBackend = new SchemaBackend();
    schemaBackend.initializeBackend(null, new DN[0]);
  }
  /**
   * Tests the {@code isLocal} method to ensure that it is considered local.
   */
  @Test()
@@ -443,6 +472,127 @@
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type that is not allowed to be altered.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddUnsupportedAttr()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClass",
         "objectClass: extensibleObject");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is not allowed to be altered.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveUnsupportedAttr()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClass",
         "objectClass: subschema",
         "-",
         "add: objectClass",
         "objectClass: extensibleObject");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove all
   * attribute type definitions.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAllAttributeTypes()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to replace all
   * attribute types.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testReplaceAllAttributeTypes()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "replace: attributeTypes");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type with a valid syntax and that isn't already defined.
   *
   * @throws  Exception  If an unexpected problem occurs.
@@ -455,10 +605,10 @@
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( 1.3.6.1.4.1.26027.1.999.4 NAME " +
              "'testAddAttributeTypeSuccessful' SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )");
         "attributeTypes: ( 1.3.6.1.4.1.26027.1.999.4 " +
              "NAME 'testAddAttributeTypeSuccessful' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORGIN 'SchemaBackendTestCase' )");
    String attrName = "testaddattributetypesuccessful";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
@@ -493,10 +643,10 @@
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypesuccessfulnooid-oid NAME " +
              "'testAddAttributeTypeSuccessfulNoOID' SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )");
         "attributeTypes: ( testaddattributetypesuccessfulnooid-oid " +
              "NAME 'testAddAttributeTypeSuccessfulNoOID' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORGIN 'SchemaBackendTestCase' )");
    String attrName = "testaddattributetypesuccessfulnooid";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
@@ -518,6 +668,143 @@
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type to a specific schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testAddAttributeTypeToAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypetoaltschemafile-oid " +
              "NAME 'testAddAttributeTypeToAltSchemaFile' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' " +
              "X-SCHEMA-FILE '98-schema-test-attrtype.ldif' )");
    String attrName = "testaddattributetypetoaltschemafile";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-attrtype.ldif");
    assertFalse(schemaFile.exists());
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type in a manner that replaces an existing definition.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddAttributeTypeSuccessfulReplace()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypesuccessfulreplace-oid " +
              "NAME 'testAddAttributeTypeSuccessfulReplace' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypesuccessfulreplace-oid " +
              "NAME 'testAddAttributeTypeSuccessfulReplace' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testaddattributetypesuccessfulreplace";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to replace an
   * attribute type definition in a custom schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public void testReplaceAttributeTypeInAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testreplaceattributetypeinaltschemafile-oid " +
              "NAME 'testReplaceAttributeTypeInAltSchemaFile' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' " +
              "X-SCHEMA-FILE '98-schema-test-replaceattrtype.ldif' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testreplaceattributetypeinaltschemafile-oid " +
              "NAME 'testReplaceAttributeTypeInAltSchemaFile' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testreplaceattributetypeinaltschemafile";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-replaceattrtype.ldif");
    assertFalse(schemaFile.exists());
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type definition that can't be parsed.
   *
   * @throws  Exception  If an unexpected problem occurs.
@@ -548,6 +835,456 @@
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type that conflicts with multiple existing types.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddAttributeTypeMultipleConflicts()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypemultipleconflicts-oid NAME " +
              "( 'testAddAttributeTypeMultipleConflicts' 'cn' 'uid' ) SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * attribute type that references an undefined superior attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddAttributeTypeUndefinedSuperior()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testaddattributetypeundefinedsuperior-oid NAME " +
              "'testAddAttributeTypeUndefinedSuperior' SUP undefined SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is defined in the server schema and does not have any
   * dependencies.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( 1.3.6.1.4.1.26027.1.999.6 NAME " +
              "'testRemoveAttributeTypeSuccessful' SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( 1.3.6.1.4.1.26027.1.999.6 NAME " +
              "'testRemoveAttributeTypeSuccessful' SYNTAX " +
              "1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN " +
              "'SchemaBackendTestCase' )");
    String attrName = "testremoveattributetypesuccessful";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type and add it back in the same modification.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveThenAddAttributeTypeSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testremovethenaddattributetypesuccessful-oid " +
              "NAME 'testRemoveThenAddAttributeTypeSuccessful' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( testremovethenaddattributetypesuccessful-oid " +
              "NAME 'testRemoveThenAddAttributeTypeSuccessful' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: attributeTypes",
         "attributeTypes: ( testremovethenaddattributetypesuccessful-oid " +
              "NAME 'testRemoveThenAddAttributeTypeSuccessful' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testremoveattributetypesuccessful";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is not defined in the server schema.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeUndefined()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( testremoveattributetypeundefined-oid " +
              "NAME 'testRemoveAttributeTypeUndefined' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testremoveattributetypeundefined";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is referenced as the superior type for another
   * attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveSuperiorAttributeType()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( 2.5.4.41 NAME 'name' EQUALITY caseIgnoreMatch " +
              "SUBSTR caseIgnoreSubstringsMatch " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{32768} " +
              "X-ORIGIN 'RFC 2256' )");
    String attrName = "name";
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is referenced by an existing objectclass.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeReferencedByObjectClass()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( 0.9.2342.19200300.100.1.1 NAME 'uid' " +
              "EQUALITY caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15{256} " +
              "X-ORIGIN 'RFC 1274' )");
    String attrName = "uid";
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is referenced by an existing name form.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeReferencedByNameForm()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testremoveattributetypereferencedbynf-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByNF' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: objectClasses",
         "objectClasses:  ( testremoveattributetypereferencedbynfoc-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByNFOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremoveattributetypereferencedbynfnf-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByNFNF' " +
              "OC testRemoveAttributeTypeReferencedByNFOC " +
              "MUST testRemoveAttributeTypeReferencedByNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( testremoveattributetypereferencedbynf-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByNF' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testremoveattributetypereferencedbynf";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is referenced by an existing DIT content rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeReferencedByDCR()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testremoveattributetypereferencedbydcr-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByDCR' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: objectClasses",
         "objectClasses:  ( testremoveattributetypereferencedbydcroc-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByDCROC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testremoveattributetypereferencedbydcroc-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByDCRDCR' " +
              "MAY testRemoveAttributeTypeReferencedByDCR " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( testremoveattributetypereferencedbydcr-oid " +
              "NAME 'testRemoveAttributeTypeReferencedByDCR' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testremoveattributetypereferencedbydcr";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * attribute type that is referenced by an existing matching rule use.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAttributeTypeReferencedByMRU()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testRemoveATRefByMRUMatch",
                                    "1.3.6.1.4.1.26027.1.999.17");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: attributeTypes",
         "attributeTypes: ( testremoveatrefbymruat-oid " +
              "NAME 'testRemoveATRefByMRUAT' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.17 " +
              "NAME 'testRemoveATRefByMRUMRU' APPLIES testRemoveATRefByMRUAT " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: attributeTypes",
         "attributeTypes: ( testremoveatrefbymruat-oid " +
              "NAME 'testRemoveATRefByMRUAT' " +
              "SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String attrName = "testremoveatrefbymruat";
    assertFalse(DirectoryServer.getSchema().hasAttributeType(attrName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testremoveatrefbymrumru"));
    assertTrue(DirectoryServer.getSchema().hasAttributeType(attrName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * objectclass that doesn't already exist, that has a valid superior class,
   * and for which all attributes contained in it are already defined.
   *
@@ -623,6 +1360,178 @@
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * objectclass to a specific schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddObjectClassToAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( testaddobjectclasstoaltschemafile-oid NAME " +
              "'testAddObjectClassToAltSchemaFile' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase' " +
              "X-SCHEMA-FILE '98-schema-test-oc.ldif' )");
    String ocName = "testaddobjectclasstoaltschemafile";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-oc.ldif");
    assertFalse(schemaFile.exists());
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * objectclass that already exists (i.e., a replace)
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddObjectClassSuccessfulReplace()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( testaddobjectclasssuccessfulreplace-oid " +
              "NAME 'testAddObjectClassSuccessfulReplace' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( testaddobjectclasssuccessfulreplace-oid " +
              "NAME 'testAddObjectClassSuccessfulReplace' SUP top STRUCTURAL " +
              "MUST cn MAY description X-ORIGIN 'SchemaBackendTestCase' )");
    String ocName = "testaddobjectclasssuccessfulreplace";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * objectclass that conflicts with multiple existing objectclasses.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddObjectClassMultipleConflicts()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( testaddobjectclassmultipleconflicts-oid " +
              "NAME ( 'testAddObjectClassMultipleConflicts' 'person' " +
              "'device' ) SUP top STRUCTURAL MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String ocName = "testaddobjectclassmultipleconflicts";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing objectclass definition and then add it back in the same operation
   * with a different definition.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveThenAddAddObjectClassSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( testremovethenaddobjectclasssuccessful-oid " +
              "NAME 'testRemoveThenAddObjectClassSuccessful' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClasses",
         "objectClasses: ( testremovethenaddobjectclasssuccessful-oid " +
              "NAME 'testRemoveThenAddObjectClassSuccessful' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: objectClasses",
         "objectClasses: ( testremovethenaddobjectclasssuccessful-oid " +
              "NAME 'testRemoveThenAddObjectClassSuccessful' SUP top " +
              "STRUCTURAL MUST cn MAY description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String ocName = "testremovethenaddobjectclasssuccessful";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * objectclass definition that can't be parsed.
   *
   * @throws  Exception  If an unexpected problem occurs.
@@ -744,5 +1653,2119 @@
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * objectclass that exists and for which there are no dependencies.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveObjectClassSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses: ( 1.3.6.1.4.1.26027.1.999.7 NAME " +
              "'testRemoveObjectClassSuccessful' SUP top STRUCTURAL MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClasses",
         "objectClasses: ( 1.3.6.1.4.1.26027.1.999.7 NAME " +
              "'testRemoveObjectClassSuccessful' SUP top STRUCTURAL MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String ocName = "testremoveobjectclasssuccessful";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * objectclass that is the superior class for another objectclass.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveSuperiorObjectClass()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClasses",
         "objectClasses: ( 2.5.6.6 NAME 'person' SUP top STRUCTURAL " +
              "MUST ( sn $ cn ) MAY ( userPassword $ telephoneNumber $ " +
              "seeAlso $ description ) X-ORIGIN 'RFC 2256' )");
    String ocName = "person";
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * objectclass that is referenced by an existing name form.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveObjectClassReferencedByNameForm()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremoveobjectclassreferencedbynf-oid " +
              "NAME 'testRemoveObjectClassReferencedByNF' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremoveattributetypereferencedbynfnf-oid " +
              "NAME 'testRemoveObjectClassReferencedByNFNF' " +
              "OC testRemoveObjectClassReferencedByNF MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClasses",
         "objectClasses:  ( testremoveobjectclassreferencedbynf-oid " +
              "NAME 'testRemoveObjectClassReferencedByNF' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')");
    String ocName = "testremoveobjectclassreferencedbynf";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * objectclass that is referenced by an existing DIT content rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveObjectClassReferencedByDCR()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremoveobjectclassreferencedbydcr-oid " +
              "NAME 'testRemoveObjectClassReferencedByDCR' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testremoveobjectclassreferencedbydcr-oid " +
              "NAME 'testRemoveObjectClassReferencedByDCRDCR' " +
              "MAY description X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: objectClasses",
         "objectClasses:  ( testremoveobjectclassreferencedbydcr-oid " +
              "NAME 'testRemoveObjectClassReferencedByDCR' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')");
    String ocName = "testremoveobjectclassreferencedbydcr";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasObjectClass(ocName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form that doesn't already exist.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformsuccessfuloc-oid " +
              "NAME 'testAddNameFormSuccessfulOC' SUP top STRUCTURAL MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( 1.3.6.1.4.1.26027.1.999.8 " +
              "NAME 'testAddNameFormSuccessful' " +
              "OC testAddNameFormSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformsuccessful";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form that doesn't already exist to an alternate schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormToAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformtoaltschemafileoc-oid " +
              "NAME 'testAddNameFormToAltSchemaFileOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddnameformtoaltschemafile-oid " +
              "NAME 'testAddNameFormToAltSchemaFile' " +
              "OC testAddNameFormToAltSchemaFileOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' " +
              "X-SCHEMA-FILE '98-schema-test-nameform.ldif' )");
    String nameFormName = "testaddnameformtoaltschemafile";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-nameform.ldif");
    assertFalse(schemaFile.exists());
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasNameForm(nameFormName));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form that references a required attribute type not defined in the server
   * schema.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormWithUndefinedReqAT()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformwithundefinedreqatoc-oid " +
              "NAME 'testAddNameFormWithUndefinedReqATOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddnameformwithundefinereqdat-oid " +
              "NAME 'testAddNameFormWithUndefinedReqAT' " +
              "OC testAddNameFormWithUndefinedReqATOC MUST xxxundefinedxxx " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformwithundefinedreqat";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form that references an optional attribute type not defined in the server
   * schema.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormWithUndefinedOptAT()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformwithundefinedoptatoc-oid " +
              "NAME 'testAddNameFormWithUndefinedOptATOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddnameformwithundefineoptdat-oid " +
              "NAME 'testAddNameFormWithUndefinedOptAT' " +
              "OC testAddNameFormWithUndefinedOptATOC MUST cn " +
              "MAY xxxundefinedxxx X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformwithundefinedoptat";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form whose structural objectclass is not defined in the server schema.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormWithUndefinedOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: nameForms",
         "nameForms: ( testaddnameformwithundefinedoc-oid " +
              "NAME 'testAddNameFormWithUndefinedOC' " +
              "OC xxxundefinedxxx MUST cn X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformwithundefinedoc";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form whose objectclass auxiliary rather than structural.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormWithAuxiliaryOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformwithauxiliaryococ-oid " +
              "NAME 'testAddNameFormWithAuxiliaryOCOC' SUP top AUXILIARY " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddnameformwithauxiliaryoc-oid " +
              "NAME 'testAddNameFormWithAuxiliaryOC' " +
              "OC testAddNameFormWithAuxiliaryOCOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformwithauxiliaryoc";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new name
   * form that references a structural objectclass already referenced by another
   * name form.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddNameFormOCConflict()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddnameformocconflictoc-oid " +
              "NAME 'testAddNameFormOCConflictOC' SUP top STRUCTURAL MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddnameformocconflict-oid " +
              "NAME 'testAddNameFormOCConflict' " +
              "OC testAddNameFormOCConflictOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: nameForms",
         "nameForms: ( testaddnameformocconflict2-oid " +
              "NAME 'testAddNameFormOCConflict2' " +
              "OC testAddNameFormOCConflictOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testaddnameformocconflict2";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing name form.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveNameFormSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremovenameformsuccessfuloc-oid " +
              "NAME 'testRemoveNameFormSuccessfulOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( 1.3.6.1.4.1.26027.1.999.9 " +
              "NAME 'testRemoveNameFormSuccessful' " +
              "OC testRemoveNameFormSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: nameForms",
         "nameForms: ( 1.3.6.1.4.1.26027.1.999.9 " +
              "NAME 'testRemoveNameFormSuccessful' " +
              "OC testRemoveNameFormSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testremovenameformsuccessful";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing name form and then add it back in the same operation.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveThenAddNameFormSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremovethenaddnameformsuccessfuloc-oid " +
              "NAME 'testRemoveThenAddNameFormSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: nameForms",
         "nameForms: ( testremovethenaddnameformsuccessful-oid " +
              "NAME 'testRemoveThenAddNameFormSuccessful' " +
              "OC testRemoveThenAddNameFormSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: nameForms",
         "nameForms: ( testremovethenaddnameformsuccessful-oid " +
              "NAME 'testRemoveThenAddNameFormSuccessful' " +
              "OC testRemoveThenAddNameFormSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: nameForms",
         "nameForms: ( testremovethenaddnameformsuccessful-oid " +
              "NAME 'testRemoveThenAddNameFormSuccessful' " +
              "OC testRemoveThenAddNameFormSuccessfulOC MUST cn MAY sn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String nameFormName = "testremovethenaddnameformsuccessful";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove a name
   * form that is referenced by a DIT structure rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveNameFormReferencedByDSR()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremovenameformreferencedbydsroc-oid " +
              "NAME 'testRemoveNameFormReferencedByDSROC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremovenameformreferencedbydsrnf-oid " +
              "NAME 'testRemoveNameFormReferencedByDSRNF' " +
              "OC testRemoveNameFormReferencedByDSROC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999009 " +
              "NAME 'testRemoveNameFormReferencedByDSRDSR' " +
              "FORM testRemoveNameFormReferencedByDSRNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: nameForms",
         "nameForms: ( testremovenameformreferencedbydsrnf-oid " +
              "NAME 'testRemoveNameFormReferencedByDSRNF' " +
              "OC testRemoveNameFormReferencedByDSROC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String nameFormName = "testremovenameformreferencedbydsrnf";
    assertFalse(DirectoryServer.getSchema().hasNameForm(nameFormName));
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasNameForm(nameFormName));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule that doesn't already exist.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentrulesuccessfuloc-oid " +
              "NAME 'testAddDITContentRuleSuccessfulOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentrulesuccessfuloc-oid " +
              "NAME 'testAddDITContentRuleSuccessful' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String ocName = "testaddditcontentrulesuccessfuloc";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    ObjectClass oc = DirectoryServer.getSchema().getObjectClass(ocName);
    assertNotNull(oc);
    DITContentRule dcr = DirectoryServer.getSchema().getDITContentRule(oc);
    assertNotNull(dcr);
    assertTrue(dcr.hasName("testaddditcontentrulesuccessful"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to replace an
   * existing DIT content rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testReplaceDITContentRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testreplaceditcontentrulesuccessfuloc-oid " +
              "NAME 'testReplaceDITContentRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testreplaceditcontentrulesuccessfuloc-oid " +
              "NAME 'testReplaceDITContentRuleSuccessful' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: ditContentRules",
         "ditContentRules: ( testreplaceditcontentrulesuccessfuloc-oid " +
              "NAME 'testReplaceDITContentRuleSuccessful' MAY sn " +
              "NOT description X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String ocName = "testreplaceditcontentrulesuccessfuloc";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    ObjectClass oc = DirectoryServer.getSchema().getObjectClass(ocName);
    assertNotNull(oc);
    DITContentRule dcr = DirectoryServer.getSchema().getDITContentRule(oc);
    assertNotNull(dcr);
    assertTrue(dcr.hasName("testreplaceditcontentrulesuccessful"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule to an alternate schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleToAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testadddcrtoaltschemafileoc-oid " +
              "NAME 'testAddDCRToAltSchemaFileOC' SUP top STRUCTURAL " +
              "MUST cn X-SCHEMA-FILE '98-schema-test-dcr.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testadddcrtoaltschemafileoc-oid " +
              "NAME 'testAddDCRToAltSchemaFile' NOT description " +
              "X-SCHEMA-FILE '98-schema-test-dcr.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String ocName = "testadddcrtoaltschemafileoc";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-dcr.ldif");
    assertFalse(schemaFile.exists());
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    ObjectClass oc = DirectoryServer.getSchema().getObjectClass(ocName);
    assertNotNull(oc);
    DITContentRule dcr = DirectoryServer.getSchema().getDITContentRule(oc);
    assertNotNull(dcr);
    assertTrue(dcr.hasName("testadddcrtoaltschemafile"));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing DIT content rule and add it back in the same operation.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveThenAddDITContentRule()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremovethenaddditcontentruleoc-oid " +
              "NAME 'testRemoveThenAddDITContentRuleOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testremovethenaddditcontentruleoc-oid " +
              "NAME 'testRemoveThenAddDITContentRule' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditContentRules",
         "ditContentRules: ( testremovethenaddditcontentruleoc-oid " +
              "NAME 'testRemoveThenAddDITContentRule' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testremovethenaddditcontentruleoc-oid " +
              "NAME 'testRemoveThenAddDITContentRule' MAY sn " +
              "NOT description X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String ocName = "testremovethenaddditcontentruleoc";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    ObjectClass oc = DirectoryServer.getSchema().getObjectClass(ocName);
    assertNotNull(oc);
    DITContentRule dcr = DirectoryServer.getSchema().getDITContentRule(oc);
    assertNotNull(dcr);
    assertTrue(dcr.hasName("testremovethenaddditcontentrule"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule whose structural objectclass is not defined in the schema.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleUndefinedOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: ditContentRules",
         "ditContentRules: ( xxxundefinedxxx-oid " +
              "NAME 'testAddDITContentRuleUndefinedOC' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule whose structural objectclass is not actually structural.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleAuxiliaryOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleauxiliaryococ-oid " +
              "NAME 'testAddDITContentRuleAuxiliaryOCOC' SUP top AUXILIARY " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleauxiliaryococ-oid " +
              "NAME 'testAddDITContentRuleAuxiliaryOC' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule whose structural objectclass is already referenced by an
   * existing DIT content rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleConflictingOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleconflictingococ-oid " +
              "NAME 'testAddDITContentRuleConflictingOCOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleconflictingococ-oid " +
              "NAME 'testAddDITContentRuleConflictingOC' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleconflictingococ-oid " +
              "NAME 'testAddDITContentRuleConflictingOC2' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule with an undefined auxiliary objectclass.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleUndefinedAuxOC()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleundefinedauxococ-oid " +
              "NAME 'testAddDITContentRuleUndefinedAuxOCOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleundefinedauxococ-oid " +
              "NAME 'testAddDITContentRuleUndefinedAuxOC' " +
              "AUX xxxundefinedxxx X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule that references an undefined required attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleUndefinedReqAT()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleundefinedreqatoc-oid " +
              "NAME 'testAddDITContentRuleAuxiliaryOCOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleundefinedreqatoc-oid " +
              "NAME 'testAddDITContentRuleUndefinedReqAT' " +
              "MUST xxxundefinedxxx X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule that references an undefined optional attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleUndefinedOptAT()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleundefinedoptatoc-oid " +
              "NAME 'testAddDITContentRuleAuxiliaryOCOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleundefinedoptatoc-oid " +
              "NAME 'testAddDITContentRuleUndefinedOptAT' " +
              "MAY xxxundefinedxxx X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new DIT
   * content rule that references an undefined prohibited attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITContentRuleUndefinedNotAT()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditcontentruleundefinednotatoc-oid " +
              "NAME 'testAddDITContentRuleAuxiliaryOCOC' SUP top STRUCTURAL " +
              "MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testaddditcontentruleundefinednotatoc-oid " +
              "NAME 'testAddDITContentRuleUndefinedNotAT' " +
              "NOT xxxundefinedxxx X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing DIT content rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveDITContentRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremoveditcontentrulesuccessfuloc-oid " +
              "NAME 'testRemoveDITContentRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: ditContentRules",
         "ditContentRules: ( testremoveditcontentrulesuccessfuloc-oid " +
              "NAME 'testRemoveDITContentRuleSuccessful' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditContentRules",
         "ditContentRules: ( testremoveditcontentrulesuccessfuloc-oid " +
              "NAME 'testRemoveDITContentRuleSuccessful' NOT description " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    String ocName = "testremoveditcontentrulesuccessfuloc";
    assertFalse(DirectoryServer.getSchema().hasObjectClass(ocName));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    ObjectClass oc = DirectoryServer.getSchema().getObjectClass(ocName);
    assertNotNull(oc);
    DITContentRule dcr = DirectoryServer.getSchema().getDITContentRule(oc);
    assertNull(dcr);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * DIT structure rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITStructureRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditstructurerulesuccessfuloc-oid " +
              "NAME 'testAddDITStructureRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddditstructurerulesuccessfulnf-oid " +
              "NAME 'testAddDITStructureRuleSuccessfulNF' " +
              "OC testAddDITStructureRuleSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999001 " +
              "NAME 'testAddDITStructureRuleSuccessful' " +
              "FORM testAddDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999001;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to replace an
   * existing DIT structure rule definition.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testReplaceDITStructureRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testreplaceditstructurerulesuccessfuloc-oid " +
              "NAME 'testReplaceDITStructureRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testreplaceditstructurerulesuccessfulnf-oid " +
              "NAME 'testReplaceDITStructureRuleSuccessfulNF' " +
              "OC testReplaceDITStructureRuleSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999002 " +
              "NAME 'testReplaceDITStructureRuleSuccessful' " +
              "FORM testReplaceDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: ditStructureRules",
         "ditStructureRules: ( 999002 " +
              "NAME 'testReplaceDITStructureRuleSuccessful' " +
              "DESC 'Testing the replacement of an existing DSR' " +
              "FORM testReplaceDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999002;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * DIT structure rule to an alternate schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITStructureRuleToAltSchemaFile()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testaddditstructureruletoaltschemafileoc-oid " +
              "NAME 'testAddDITStructureRuleToAltSchemaFileOC' SUP top " +
              "STRUCTURAL MUST cn X-SCHEMA-FILE '98-schema-test-dsr.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testaddditstructureruletoaltschemafilenf-oid " +
              "NAME 'testAddDITStructureRuleToAltSchemaFileNF' " +
              "OC testAddDITStructureRuleToAltSchemaFileOC MUST cn " +
              "X-SCHEMA-FILE '98-schema-test-dsr.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999010 " +
              "NAME 'testAddDITStructureRuleToAltSchemaFile' " +
              "FORM testAddDITStructureRuleToAltSchemaFileNF " +
              "X-SCHEMA-FILE '98-schema-test-dsr.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999010;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-dsr.ldif");
    assertFalse(schemaFile.exists());
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing DIT structure rule definition and add it back in the same
   * operation.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAndAddDITStructureRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremoveandaddditstructurerulesuccessfuloc-oid " +
              "NAME 'testRemoveAndAddDITStructureRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremoveandaddditstructurerulesuccessfulnf-oid " +
              "NAME 'testRemoveAndAddDITStructureRuleSuccessfulNF' " +
              "OC testRemoveAndAddDITStructureRuleSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999003 " +
              "NAME 'testRemoveAndAddDITStructureRuleSuccessful' " +
              "FORM testRemoveAndAddDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditStructureRules",
         "ditStructureRules: ( 999003 " +
              "NAME 'testRemoveAndAddDITStructureRuleSuccessful' " +
              "FORM testRemoveAndAddDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999003 " +
              "NAME 'testRemoveAndAddDITStructureRuleSuccessful' " +
              "DESC 'Testing removing and re-adding an existing DSR' " +
              "FORM testRemoveAndAddDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999003;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertTrue(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * DIT structure rule with an undefined name form.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITStructureRuleUndefinedNameForm()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: ditStructureRules",
         "ditStructureRules: ( 999004 " +
              "NAME 'testAddDITStructureRuleUndefinedNameForm' " +
              "FORM xxxundefinedxxx " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999004;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * DIT structure rule that references an undefined superior rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddDITStructureRuleUndefinedSuperior()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testadddsrundefinedsuperioroc-oid " +
              "NAME 'testAddDSRUndefinedSuperiorOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testadddsrundefinedsuperiornf-oid " +
              "NAME 'testAddDSRUndefinedSuperiorNF' " +
              "OC testAddDSRUndefinedSuperiorOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999005 " +
              "NAME 'testAddDSRUndefinedSuperior' " +
              "FORM testAddDSRUndefinedSuperiorNF SUP 999000 " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999005;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing DIT structure rule definition.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveDITStructureRuleSuccessful()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremoveditstructurerulesuccessfuloc-oid " +
              "NAME 'testRemoveDITStructureRuleSuccessfulOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremoveditstructurerulesuccessfulnf-oid " +
              "NAME 'testRemoveDITStructureRuleSuccessfulNF' " +
              "OC testRemoveDITStructureRuleSuccessfulOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999006 " +
              "NAME 'testRemoveDITStructureRuleSuccessful' " +
              "FORM testRemoveDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditStructureRules",
         "ditStructureRules: ( 999006 " +
              "NAME 'testRemoveDITStructureRuleSuccessful' " +
              "FORM testRemoveDITStructureRuleSuccessfulNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999006;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing DIT structure rule definition which is the superior rule for
   * another DIT structure rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveSuperiorDITStructureRule()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: objectClasses",
         "objectClasses:  ( testremovesuperiorditstructureruleoc-oid " +
              "NAME 'testRemoveSuperiorDITStructureRuleOC' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "objectClasses:  ( testremovesuperiorditstructureruleoc2-oid " +
              "NAME 'testRemoveSuperiorDITStructureRuleOC2' SUP top " +
              "STRUCTURAL MUST cn X-ORIGIN 'SchemaBackendTestCase')",
         "-",
         "add: nameForms",
         "nameForms: ( testremovesuperiorditstructurerulenf-oid " +
              "NAME 'testRemoveSuperiorDITStructureRuleNF' " +
              "OC testRemoveSuperiorDITStructureRuleOC MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "nameForms: ( testremovesuperiorditstructurerulenf2-oid " +
              "NAME 'testRemoveSuperiorDITStructureRuleNF2' " +
              "OC testRemoveSuperiorDITStructureRuleOC2 MUST cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: ditStructureRules",
         "ditStructureRules: ( 999007 " +
              "NAME 'testRemoveSuperiorDITStructureRule' " +
              "FORM testRemoveSuperiorDITStructureRuleNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "ditStructureRules: ( 999008 " +
              "NAME 'testRemoveSuperiorDITStructureRule2' " +
              "FORM testRemoveSuperiorDITStructureRuleNF2 SUP 999007 " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditStructureRules",
         "ditStructureRules: ( 999007 " +
              "NAME 'testRemoveSuperiorDITStructureRule' " +
              "FORM testRemoveSuperiorDITStructureRuleNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    int ruleID = 999007;
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    assertTrue(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
    path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "delete: ditStructureRules",
         "ditStructureRules: ( 999008 " +
              "NAME 'testRemoveSuperiorDITStructureRule2' " +
              "FORM testRemoveSuperiorDITStructureRuleNF2 SUP 999007 " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "ditStructureRules: ( 999007 " +
              "NAME 'testRemoveSuperiorDITStructureRule' " +
              "FORM testRemoveSuperiorDITStructureRuleNF " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    args = new String[]
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    assertFalse(DirectoryServer.getSchema().hasDITStructureRule(ruleID));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * matching rule use that doesn't already exist.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMatchingRuleUseSuccessful()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testAddMRUSuccessfulMatch",
                                    "1.3.6.1.4.1.26027.1.999.10");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.10 " +
              "NAME 'testAddMRUSuccessful' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testaddmrusuccessful"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * matching rule to an alternate schema file.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMatchingRuleUseToAltSchemaFile()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testAddMRUToAltSchemaFileMatch",
                                    "1.3.6.1.4.1.26027.1.999.18");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.18 " +
              "NAME 'testAddMRUToAltSchemaFile' APPLIES cn " +
              "X-SCHEMA-FILE '98-schema-test-mru.ldif' " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    File schemaFile = new File(SchemaConfigManager.getSchemaDirectoryPath(),
                               "98-schema-test-mru.ldif");
    assertFalse(schemaFile.exists());
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testaddmrutoaltschemafile"));
    assertTrue(schemaFile.exists());
  }
  /**
   * Tests the behavior of the schema backend when attempting to replace an
   * existing matching rule use.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testReplaceMatchingRuleUseSuccessful()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testReplaceMRUSuccessfulMatch",
                                    "1.3.6.1.4.1.26027.1.999.11");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.11 " +
              "NAME 'testReplaceMRUSuccessful' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.11 " +
              "NAME 'testReplaceMRUSuccessful' APPLIES ( cn $ sn ) " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testreplacemrusuccessful"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove and
   * re-add an existing matching rule use in the same operation.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveAndAddMatchingRuleUse()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testRemoveAndAddMRUMatch",
                                    "1.3.6.1.4.1.26027.1.999.12");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.12 " +
              "NAME 'testRemoveAndAddMRU' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.12 " +
              "NAME 'testRemoveAndAddMRU' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "-",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.12 " +
              "NAME 'testRemoveAndAddMRU' APPLIES ( cn $ sn ) " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testremoveandaddmru"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a matching
   * rule use that references the same matching rule as another matching rule
   * use.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMatchingRuleUseMRConflict()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testAddMRUMRConflictMatch",
                                    "1.3.6.1.4.1.26027.1.999.14");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.14 " +
              "NAME 'testAddMRUMRConflict' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.14 " +
              "NAME 'testAddMRUMRConflict2' APPLIES sn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNotNull(mru);
    assertTrue(mru.hasName("testaddmrumrconflict"));
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * matching rule use that references an undefined matching rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMatchingRuleUseMRUndefined()
         throws Exception
  {
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.15 " +
              "NAME 'testAddMRUMRUndefined' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to add a new
   * matching rule use that references an undefined attribute type.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testAddMatchingRuleUseAttributeTypeUndefined()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testAddMRUATUndefinedMatch",
                                    "1.3.6.1.4.1.26027.1.999.16");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.16 " +
              "NAME 'testAddMatchingRuleUseATUndefined' " +
              "APPLIES xxxundefinedxxx " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertFalse(LDAPModify.mainModify(args, false, null, null) == 0);
  }
  /**
   * Tests the behavior of the schema backend when attempting to remove an
   * existing matching rule use.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testRemoveMatchingRuleUseSuccessful()
         throws Exception
  {
    SchemaTestMatchingRule matchingRule =
         new SchemaTestMatchingRule("testRemoveMRUSuccessfulMatch",
                                    "1.3.6.1.4.1.26027.1.999.13");
    DirectoryServer.registerMatchingRule(matchingRule, false);
    String path = TestCaseUtils.createTempFile(
         "dn: cn=schema",
         "changetype: modify",
         "add: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.13 " +
              "NAME 'testRemoveMRUSuccessful' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )",
         "",
         "dn: cn=schema",
         "changetype: modify",
         "delete: matchingRuleUse",
         "matchingRuleUse: ( 1.3.6.1.4.1.26027.1.999.13 " +
              "NAME 'testRemoveMRUSuccessful' APPLIES cn " +
              "X-ORIGIN 'SchemaBackendTestCase' )");
    assertFalse(DirectoryServer.getSchema().hasMatchingRuleUse(matchingRule));
    String[] args =
    {
      "-h", "127.0.0.1",
      "-p", String.valueOf(TestCaseUtils.getServerLdapPort()),
      "-D", "cn=Directory Manager",
      "-w", "password",
      "-f", path
    };
    assertEquals(LDAPModify.mainModify(args, false, null, System.err), 0);
    MatchingRuleUse mru =
         DirectoryServer.getSchema().getMatchingRuleUse(matchingRule);
    assertNull(mru);
  }
  /**
   * Tests the {@code exportLDIF} method with a valid configuration.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testExportLDIF()
         throws Exception
  {
    DN configEntryDN =
            DN.decode("ds-cfg-backend-id=schema,cn=Backends,cn=config");
    DN[] baseDNs = { DN.decode("cn=schema") };
    ConfigEntry configEntry =
         DirectoryServer.getConfigHandler().getConfigEntry(configEntryDN);
    File tempFile = File.createTempFile("schema", "testExportLDIF");
    tempFile.deleteOnExit();
    LDIFExportConfig exportConfig =
         new LDIFExportConfig(tempFile.getAbsolutePath(),
                              ExistingFileBehavior.OVERWRITE);
    schemaBackend.exportLDIF(configEntry, baseDNs, exportConfig);
    assertTrue(tempFile.exists());
    assertTrue(tempFile.length() > 0);
  }
  /**
   * Tests the {@code importLDIF} method to ensure that it throws an exception.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test(expectedExceptions = { DirectoryException.class })
  public void testImportLDIF()
         throws Exception
  {
    DN configEntryDN =
            DN.decode("ds-cfg-backend-id=schema,cn=Backends,cn=config");
    DN[] baseDNs = { DN.decode("cn=schema") };
    ConfigEntry configEntry =
         DirectoryServer.getConfigHandler().getConfigEntry(configEntryDN);
    File tempFile = File.createTempFile("schema", "testImportLDIF");
    tempFile.deleteOnExit();
    LDIFImportConfig importConfig =
         new LDIFImportConfig(tempFile.getAbsolutePath());
    schemaBackend.importLDIF(configEntry, baseDNs, importConfig);
  }
  /**
   * Tests the {@code getComponentEntryDN} method.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  @Test()
  public void testGetComponentEntryDN()
         throws Exception
  {
    DN configEntryDN =
            DN.decode("ds-cfg-backend-id=schema,cn=Backends,cn=config");
    assertEquals(schemaBackend.getComponentEntryDN(), configEntryDN);
  }
  /**
   * Tests the {@code getClassName} method.
   */
  @Test()
  public void testGetClassName()
  {
    assertEquals(schemaBackend.getClassName(), SchemaBackend.class.getName());
  }
  /**
   * Tests the {@code getAlerts} method.
   */
  @Test()
  public void testGetAlerts()
  {
    LinkedHashMap<String,String> alerts = schemaBackend.getAlerts();
    assertNotNull(alerts);
    assertFalse(alerts.isEmpty());
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/SchemaTestMatchingRule.java
New file
@@ -0,0 +1,190 @@
/*
 * 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 2007 Sun Microsystems, Inc.
 */
package org.opends.server.backends;
import org.opends.server.api.EqualityMatchingRule;
import org.opends.server.config.ConfigEntry;
import org.opends.server.config.ConfigException;
import org.opends.server.schema.CaseIgnoreEqualityMatchingRule;
import org.opends.server.types.ByteString;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
/**
 * This class implements an equality matching rule that is intended for testing
 * purposes within the server (e.g., in conjunction with matching rule uses).
 * For all practical purposes, it behaves like the standard caseIgnoreMatch
 * matching rule.
 */
public class SchemaTestMatchingRule
       extends EqualityMatchingRule
{
  // The matching rule that will do all the real work behind the scenes.
  private CaseIgnoreEqualityMatchingRule caseIgnoreMatchingRule;
  // The name for this matching rule.
  private String name;
  // The OID for this matching rule.
  private String oid;
  /**
   * Creates a new instance of this matching rule with the provided information.
   *
   * @param  name  The name to use for this matching rule.
   * @param  oid   The OID to use for this matching rule.
   *
   * @throws  Exception  If an unexpected problem occurs.
   */
  public SchemaTestMatchingRule(String name, String oid)
         throws Exception
  {
    super();
    this.name = name;
    this.oid  = oid;
    caseIgnoreMatchingRule = new CaseIgnoreEqualityMatchingRule();
    caseIgnoreMatchingRule.initializeMatchingRule(null);
  }
  /**
   * Initializes this matching rule based on the information in the provided
   * configuration entry.
   *
   * @param  configEntry  The configuration entry that contains the information
   *                      to use to initialize this matching rule.
   *
   * @throws  ConfigException  If an unrecoverable problem arises in the
   *                           process of performing the initialization.
   *
   * @throws  InitializationException  If a problem that is not
   *                                   configuration-related occurs during
   *                                   initialization.
   */
  public void initializeMatchingRule(ConfigEntry configEntry)
         throws ConfigException, InitializationException
  {
    // No initialization is required.
  }
  /**
   * Retrieves the common name for this matching rule.
   *
   * @return  The common name for this matching rule, or <CODE>null</CODE> if
   * it does not have a name.
   */
  public String getName()
  {
    return name;
  }
  /**
   * Retrieves the OID for this matching rule.
   *
   * @return  The OID for this matching rule.
   */
  public String getOID()
  {
    return oid;
  }
  /**
   * Retrieves the description for this matching rule.
   *
   * @return  The description for this matching rule, or <CODE>null</CODE> if
   *          there is none.
   */
  public String getDescription()
  {
    return null;
  }
  /**
   * Retrieves the OID of the syntax with which this matching rule is
   * associated.
   *
   * @return  The OID of the syntax with which this matching rule is associated.
   */
  public String getSyntaxOID()
  {
    return caseIgnoreMatchingRule.getSyntaxOID();
  }
  /**
   * Retrieves the normalized form of the provided value, which is best suited
   * for efficiently performing matching operations on that value.
   *
   * @param  value  The value to be normalized.
   *
   * @return  The normalized version of the provided value.
   *
   * @throws  DirectoryException  If the provided value is invalid according to
   *                              the associated attribute syntax.
   */
  public ByteString normalizeValue(ByteString value)
         throws DirectoryException
  {
    return caseIgnoreMatchingRule.normalizeValue(value);
  }
  /**
   * Indicates whether the two provided normalized values are equal to each
   * other.
   *
   * @param  value1  The normalized form of the first value to compare.
   * @param  value2  The normalized form of the second value to compare.
   *
   * @return  <CODE>true</CODE> if the provided values are equal, or
   *          <CODE>false</CODE> if not.
   */
  public boolean areEqual(ByteString value1, ByteString value2)
  {
    return caseIgnoreMatchingRule.areEqual(value1, value2);
  }
}
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestAttributeType.java
@@ -22,13 +22,15 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import java.util.Collection;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -139,12 +141,157 @@
    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);
      StringBuilder definition = new StringBuilder();
      definition.append("( ");
      definition.append(oid);
      LinkedHashSet<String> nameSet = new LinkedHashSet<String>();
      if (primaryName != null)
      {
        nameSet.add(primaryName);
      }
      if (names != null)
      {
        for (String name : names)
        {
          nameSet.add(name);
        }
      }
      if (! nameSet.isEmpty())
      {
        if (nameSet.size() == 1)
        {
          definition.append(" NAME '");
          definition.append(nameSet.iterator().next());
          definition.append("'");
        }
        else
        {
          Iterator<String> iterator = nameSet.iterator();
          definition.append(" NAME ( '");
          definition.append(iterator.next());
          while (iterator.hasNext())
          {
            definition.append("' '");
            definition.append(iterator.next());
          }
          definition.append("' )");
        }
      }
      if (description != null)
      {
        definition.append(" DESC '");
        definition.append(description);
        definition.append("'");
      }
      if (isObsolete)
      {
        definition.append(" OBSOLETE");
      }
      if (superiorType != null)
      {
        definition.append(" SUP ");
        definition.append(superiorType.getNameOrOID());
      }
      if (equalityMatchingRule != null)
      {
        definition.append(" EQUALITY ");
        definition.append(equalityMatchingRule.getNameOrOID());
      }
      if (orderingMatchingRule != null)
      {
        definition.append(" ORDERING ");
        definition.append(orderingMatchingRule.getNameOrOID());
      }
      if (substringMatchingRule != null)
      {
        definition.append(" SUBSTR ");
        definition.append(substringMatchingRule.getNameOrOID());
      }
      if (syntax != null)
      {
        definition.append(" SYNTAX ");
        definition.append(syntax.getOID());
      }
      if (isSingleValue)
      {
        definition.append(" SINGLE-VALUE");
      }
      if (isCollective)
      {
        definition.append(" COLLECTIVE");
      }
      if (isNoUserModification)
      {
        definition.append(" NO-USER-MODIFICATIOn");
      }
      if (attributeUsage != null)
      {
        definition.append(" USAGE ");
        definition.append(attributeUsage.toString());
      }
      if (extraProperties != null)
      {
        for (String property : extraProperties.keySet())
        {
          List<String> values = extraProperties.get(property);
          if ((values == null) || values.isEmpty())
          {
            continue;
          }
          else if (values.size() == 1)
          {
            definition.append(" ");
            definition.append(property);
            definition.append(" '");
            definition.append(values.get(0));
            definition.append("'");
          }
          else
          {
            definition.append(" ");
            definition.append(property);
            definition.append(" (");
            for (String value : values)
            {
              definition.append(" '");
              definition.append(value);
              definition.append("'");
            }
            definition.append(" )");
          }
        }
      }
      definition.append(" )");
      return new AttributeType(definition.toString(), primaryName, names, oid,
                               description, superiorType, syntax,
                               approximateMatchingRule, equalityMatchingRule,
                               orderingMatchingRule, substringMatchingRule,
                               attributeUsage, isCollective,
                               isNoUserModification, isObsolete, isSingleValue,
                               extraProperties);
    }
@@ -283,7 +430,7 @@
   */
  @Test(expectedExceptions = NullPointerException.class)
  public void testSimpleConstructorNPE() throws Exception {
    new AttributeType(null, null, null, null, null, null, null,
    new AttributeType(null, null, null, null, null, null, null, null,
        false, false, false, false);
  }
@@ -298,7 +445,7 @@
   */
  @Test(expectedExceptions = NullPointerException.class)
  public void testComplexConstructorNPE() throws Exception {
    new AttributeType(null, null, null, null, null, null, null, null,
    new AttributeType(null, null, null, null, null, null, null, null, null,
        null, null, null, false, false, false, false, null);
  }
@@ -313,7 +460,7 @@
   */
  @Test
  public void testComplexConstructorDefault() throws Exception {
    AttributeType type = new AttributeType(null, null, "1.2.3", null,
    AttributeType type = new AttributeType("", null, null, "1.2.3", null,
        null, null, null, null, null, null, null, false, false,
        false, false, null);
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestObjectClass.java
@@ -22,7 +22,7 @@
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 *      Portions Copyright 2006-2007 Sun Microsystems, Inc.
 */
package org.opends.server.types;
@@ -31,6 +31,7 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
@@ -112,9 +113,163 @@
    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);
      StringBuilder definition = new StringBuilder();
      definition.append("( ");
      definition.append(oid);
      LinkedHashSet<String> nameSet = new LinkedHashSet<String>();
      if (primaryName != null)
      {
        nameSet.add(primaryName);
      }
      if (names != null)
      {
        for (String name : names)
        {
          nameSet.add(name);
        }
      }
      if (! nameSet.isEmpty())
      {
        if (nameSet.size() == 1)
        {
          definition.append(" NAME '");
          definition.append(nameSet.iterator().next());
          definition.append("'");
        }
        else
        {
          Iterator<String> iterator = nameSet.iterator();
          definition.append(" NAME ( '");
          definition.append(iterator.next());
          while (iterator.hasNext())
          {
            definition.append("' '");
            definition.append(iterator.next());
          }
          definition.append("' )");
        }
      }
      if (description != null)
      {
        definition.append(" DESC '");
        definition.append(description);
        definition.append("'");
      }
      if (isObsolete)
      {
        definition.append(" OBSOLETE");
      }
      if (superior != null)
      {
        definition.append(" SUP ");
        definition.append(superior.getNameOrOID());
      }
      if (objectClassType != null)
      {
        definition.append(" ");
        definition.append(objectClassType.toString());
      }
      if ((requiredAttributeTypes != null) &&
          (! requiredAttributeTypes.isEmpty()))
      {
        if (requiredAttributeTypes.size() == 1)
        {
          definition.append(" MUST ");
          definition.append(
               requiredAttributeTypes.iterator().next().getNameOrOID());
        }
        else
        {
          Iterator<AttributeType> iterator = requiredAttributeTypes.iterator();
          definition.append(" MUST ( ");
          definition.append(iterator.next().getNameOrOID());
          while (iterator.hasNext())
          {
            definition.append(" $ ");
            definition.append(iterator.next().getNameOrOID());
          }
          definition.append(" )");
        }
      }
      if ((optionalAttributeTypes != null) &&
          (! optionalAttributeTypes.isEmpty()))
      {
        if (optionalAttributeTypes.size() == 1)
        {
          definition.append(" MUST ");
          definition.append(
               optionalAttributeTypes.iterator().next().getNameOrOID());
        }
        else
        {
          Iterator<AttributeType> iterator = optionalAttributeTypes.iterator();
          definition.append(" MUST ( ");
          definition.append(iterator.next().getNameOrOID());
          while (iterator.hasNext())
          {
            definition.append(" $ ");
            definition.append(iterator.next().getNameOrOID());
          }
          definition.append(" )");
        }
      }
      if (extraProperties != null)
      {
        for (String property : extraProperties.keySet())
        {
          List<String> values = extraProperties.get(property);
          if ((values == null) || values.isEmpty())
          {
            continue;
          }
          else if (values.size() == 1)
          {
            definition.append(" ");
            definition.append(property);
            definition.append(" '");
            definition.append(values.get(0));
            definition.append("'");
          }
          else
          {
            definition.append(" ");
            definition.append(property);
            definition.append(" (");
            for (String value : values)
            {
              definition.append(" '");
              definition.append(value);
              definition.append("'");
            }
            definition.append(" )");
          }
        }
      }
      definition.append(" )");
      return new ObjectClass(definition.toString(), primaryName, names, oid,
                             description, superior, requiredAttributeTypes,
                             optionalAttributeTypes, objectClassType,
                             isObsolete, extraProperties);
    }
@@ -219,7 +374,7 @@
    Set<AttributeType> emptySet = Collections.emptySet();
    Map<String, List<String>> emptyMap = Collections.emptyMap();
    new ObjectClass("test", Collections.singleton("test"), null,
    new ObjectClass(null, "test", Collections.singleton("test"), null,
        "description", DirectoryServer.getTopObjectClass(), emptySet,
        emptySet, ObjectClassType.STRUCTURAL, false, emptyMap);
  }
@@ -235,7 +390,8 @@
   */
  @Test
  public void testConstructorDefault() throws Exception {
    ObjectClass type = new ObjectClass(null, null, "1.2.3", null,
    String definition = "( 1.2.3 )";
    ObjectClass type = new ObjectClass(definition, null, null, "1.2.3", null,
        null, null, null, null, false, null);
    Assert.assertNull(type.getPrimaryName());