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

matthew_swift
03.39.2006 59685c84348f8d71dc3bbd3513c5eb10cc738a8e
Refactor DN and RDN classes and improve their test coverage.

The following refactoring work was performed:

* removed the DN, RDN, and AVA comparator classes. The DN
and RDN classes implement Comparable. RDN.compareTo now
uses ordering matching rules where possible (taken from
the original RDN Comparator class)

* RDN class is now almost immutable (it's intention is to
be 100% immutable). It is still possible to retrieve
attribute values and modify them (attribute values should
be immutable - issue raised)

* RDN compareTo, hashCode, and equals methods now
sort AVAs based on the type's name/oid (previously no
sorting was performed)

* provide factory methods for simple RDNs and a builder
class for constructing more complex RDNs

* DN class is now immutable with the exception described above
for RDN attribute values. There are now factory methods for
creating, joining, and splitting DNs

* special factory method for creating null DNs - DN.nullDN()

* DN.getParent() renamed to DN.getParentDNInSuffix() - all references
updated - this method will be moved in a subsequent change
to the DirectoryServer class

* implemented a new DN.getParent() method which ignores suffixes

* DN no longer copy their RDN array when constructing parent
DNs or localNames - instead array offset+length is identifies
which part of the array is associated with the DN

* refactored and simplified much of the DN/RDN decoding routines
3 files deleted
1 files added
77 files modified
9384 ■■■■ changed files
opends/src/server/org/opends/server/backends/BackupBackend.java 30 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/MemoryBackend.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/MonitorBackend.java 14 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/RootDSEBackend.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/SchemaBackend.java 15 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/DN2URI.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java 34 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/ImportJob.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/RootContainer.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/jeb/VerifyJob.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskBackend.java 10 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/task/TaskScheduler.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/AddOperation.java 37 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/CertificateMapperConfigManager.java 7 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/CompareOperation.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DeleteOperation.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/DirectoryServer.java 33 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/EntryCacheConfigManager.java 6 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/KeyManagerProviderConfigManager.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ModifyDNOperation.java 27 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/ModifyOperation.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/Operation.java 3 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/PersistentSearch.java 10 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/RFC3672SubtreeSpecification.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/RelativeSubtreeSpecification.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/SimpleSubtreeSpecification.java 10 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/core/TrustManagerProviderConfigManager.java 8 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/messages/CoreMessages.java 13 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java 4 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/OctetStringOrderingMatchingRule.java 35 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/RFC3672SubtreeSpecificationSyntax.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/schema/RelativeSubtreeSpecificationSyntax.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/MultimasterSynchronization.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/synchronization/SynchronizationDomain.java 10 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/LDIFDiff.java 10 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/LDIFModify.java 6 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/LDIFSearch.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/makeldif/DNTag.java 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java 29 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/makeldif/UnderscoreDNTag.java 27 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/tools/makeldif/UnderscoreParentDNTag.java 8 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/AttributeValue.java 116 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/AttributeValueComparator.java 181 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/DN.java 3122 ●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/DNComparator.java 128 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/Entry.java 24 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/LDAPURL.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/RDN.java 2491 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/RDNComparator.java 132 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/types/SearchFilter.java 16 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/LDIFReader.java 2 ●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/util/StaticUtils.java 99 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/JebTestCase.java 4 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java 34 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java 4 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java 24 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java 72 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestRFC3672SubtreeSpecification.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestRelativeSubtreeSpecification.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/AnonymousSASLMechanismHandlerTestCase.java 6 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandlerTestCase.java 14 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java 6 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java 8 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java 8 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalSearchOperationTestCase.java 4 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java 8 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java 9 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java 9 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java 9 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java 9 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java 1379 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java 2 ●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java 869 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestAddChangeRecordEntry.java 12 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestChangeRecordEntry.java 7 ●●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestDeleteChangeRecordEntry.java 8 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestModifyChangeRecordEntry.java 13 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestModifyDNChangeRecordEntry.java 19 ●●●● patch | view | raw | blame | history
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestStaticUtils.java 59 ●●●●● patch | view | raw | blame | history
opends/src/server/org/opends/server/backends/BackupBackend.java
@@ -245,19 +245,20 @@
    LinkedHashMap<AttributeType,List<Attribute>> userAttrs =
         new LinkedHashMap<AttributeType,List<Attribute>>(1);
    AttributeType[]  attrTypes  = backupBaseDN.getRDN().getAttributeTypes();
    AttributeValue[] attrValues = backupBaseDN.getRDN().getAttributeValues();
    for (int i=0; i < attrTypes.length; i++)
    RDN rdn = backupBaseDN.getRDN();
    int numAVAs = rdn.getNumValues();
    for (int i=0; i < numAVAs; i++)
    {
      LinkedHashSet<AttributeValue> valueSet =
           new LinkedHashSet<AttributeValue>(1);
      valueSet.add(attrValues[i]);
      valueSet.add(rdn.getAttributeValue(i));
      AttributeType attrType = rdn.getAttributeType(i);
      ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
      attrList.add(new Attribute(attrTypes[i], attrTypes[i].getNameOrOID(),
      attrList.add(new Attribute(attrType, attrType.getNameOrOID(),
                                 valueSet));
      userAttrs.put(attrTypes[i], attrList);
      userAttrs.put(attrType, attrList);
    }
    backupBaseEntry = new Entry(backupBaseDN, objectClasses, userAttrs,
@@ -370,7 +371,7 @@
    // If so, then it must point to a backup directory.  Otherwise, it must be
    // two levels below the backup base entry and must point to a specific
    // backup.
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      int    msgID   = MSGID_BACKUP_INVALID_BASE;
@@ -381,7 +382,7 @@
    {
      return getBackupDirectoryEntry(entryDN);
    }
    else if (backupBaseDN.equals(parentDN.getParent()))
    else if (backupBaseDN.equals(parentDN.getParentDNInSuffix()))
    {
      return getBackupEntry(entryDN);
    }
@@ -528,7 +529,7 @@
    // Next, get the backup directory from the parent DN.
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      int    msgID   = MSGID_BACKUP_NO_BACKUP_PARENT_DN;
@@ -929,7 +930,7 @@
        }
      }
    }
    else if (backupBaseDN.equals(parentDN = baseDN.getParent()))
    else if (backupBaseDN.equals(parentDN = baseDN.getParentDNInSuffix()))
    {
      Entry backupDirEntry = getBackupDirectoryEntry(baseDN);
@@ -983,7 +984,8 @@
    }
    else
    {
      if ((parentDN == null) || (! backupBaseDN.equals(parentDN.getParent())))
      if ((parentDN == null)
          || (! backupBaseDN.equals(parentDN.getParentDNInSuffix())))
      {
        int    msgID   = MSGID_BACKUP_NO_SUCH_ENTRY;
        String message = getMessage(msgID);
@@ -1475,13 +1477,9 @@
  public static DN makeChildDN(DN parentDN, AttributeType rdnAttrType,
                               String rdnStringValue)
  {
    RDN[] baseComponents = parentDN.getRDNComponents();
    RDN[] components = new RDN[baseComponents.length+1];
    AttributeValue attrValue =
         new AttributeValue(rdnAttrType, rdnStringValue);
    components[0] = new RDN(rdnAttrType, attrValue);
    System.arraycopy(baseComponents, 0, components, 1, baseComponents.length);
    return new DN(components);
    return parentDN.concat(RDN.create(rdnAttrType, attrValue));
  }
}
opends/src/server/org/opends/server/backends/MemoryBackend.java
@@ -292,7 +292,7 @@
    // Get the parent DN and ensure that it exists in the backend.
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      int    msgID   = MSGID_MEMORYBACKEND_ENTRY_DOESNT_BELONG;
@@ -393,7 +393,7 @@
    childDNs.remove(entryDN);
    entryMap.remove(entryDN);
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN != null)
    {
      HashSet<DN> parentsChildren = childDNs.get(parentDN);
@@ -505,7 +505,7 @@
    // Make sure that the parent of the new entry exists.
    DN parentDN = entry.getDN().getParent();
    DN parentDN = entry.getDN().getParentDNInSuffix();
    if ((parentDN == null) || (! entryMap.containsKey(parentDN)))
    {
      int    msgID   = MSGID_MEMORYBACKEND_RENAME_PARENT_DOESNT_EXIST;
opends/src/server/org/opends/server/backends/MonitorBackend.java
@@ -371,7 +371,7 @@
    // See if the monitor base entry is the immediate parent for the requested
    // entry.  If not, then throw an exception.
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if ((parentDN == null) || (! parentDN.equals(baseMonitorDN)))
    {
      if (baseMonitorDN.isAncestorOf(entryDN))
@@ -405,7 +405,7 @@
    // Get the RDN value and see if it matches the instance name for one of
    // the directory server monitor providers.
    String rdnValue = entryRDN.getAttributeValues()[0].getStringValue();
    String rdnValue = entryRDN.getAttributeValue(0).getStringValue();
    MonitorProvider monitorProvider =
         DirectoryServer.getMonitorProvider(rdnValue.toLowerCase());
    if (monitorProvider == null)
@@ -448,7 +448,7 @@
      return true;
    }
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if ((parentDN == null) || (! parentDN.equals(baseMonitorDN)))
    {
      return false;
@@ -460,7 +460,7 @@
      return false;
    }
    String rdnValue = rdn.getAttributeValues()[0].getStringValue();
    String rdnValue = rdn.getAttributeValue(0).getStringValue();
    MonitorProvider monitorProvider =
         DirectoryServer.getMonitorProvider(toLowerCase(rdnValue));
    return (monitorProvider != null);
@@ -654,14 +654,14 @@
    // Make sure to include the RDN attribute.
    RDN            entryRDN = entryDN.getRDN();
    AttributeType  rdnType  = entryRDN.getAttributeTypes()[0];
    AttributeValue rdnValue = entryRDN.getAttributeValues()[0];
    AttributeType  rdnType  = entryRDN.getAttributeType(0);
    AttributeValue rdnValue = entryRDN.getAttributeValue(0);
    LinkedHashSet<AttributeValue> rdnValues =
         new LinkedHashSet<AttributeValue>(1);
    rdnValues.add(rdnValue);
    Attribute rdnAttr = new Attribute(rdnType, entryRDN.getAttributeNames()[0],
    Attribute rdnAttr = new Attribute(rdnType, entryRDN.getAttributeName(0),
                                      rdnValues);
    ArrayList<Attribute> rdnList = new ArrayList<Attribute>(1);
    rdnList.add(rdnAttr);
opends/src/server/org/opends/server/backends/RootDSEBackend.java
@@ -72,7 +72,6 @@
import org.opends.server.types.LDIFExportConfig;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.ObjectClass;
import org.opends.server.types.RDN;
import org.opends.server.types.RestoreConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchFilter;
@@ -230,7 +229,7 @@
    // Create the set of base DNs that we will handle.  In this case, it's just
    // the root DSE.
    rootDSEDN    = new DN(new ArrayList<RDN>(0));
    rootDSEDN    = DN.nullDN();
    this.baseDNs = new DN[] { rootDSEDN };
opends/src/server/org/opends/server/backends/SchemaBackend.java
@@ -399,19 +399,16 @@
    RDN rdn = entryDN.getRDN();
    if (rdn != null)
    {
      String[]         rdnNames  = rdn.getAttributeNames();
      AttributeType[]  rdnTypes  = rdn.getAttributeTypes();
      AttributeValue[] rdnValues = rdn.getAttributeValues();
      int numRDNs = rdnTypes.length;
      for (int i=0; i < numRDNs; i++)
      int numAVAs = rdn.getNumValues();
      for (int i=0; i < numAVAs; i++)
      {
        LinkedHashSet<AttributeValue> valueSet =
             new LinkedHashSet<AttributeValue>(1);
        valueSet.add(rdnValues[i]);
        valueSet.add(rdn.getAttributeValue(i));
        AttributeType attrType = rdnTypes[i];
        Attribute a = new Attribute(attrType, rdnNames[i], valueSet);
        AttributeType attrType = rdn.getAttributeType(i);
        String attrName = rdn.getAttributeName(i);
        Attribute a = new Attribute(attrType, attrName, valueSet);
        ArrayList<Attribute> attrList = new ArrayList<Attribute>(1);
        attrList.add(a);
opends/src/server/org/opends/server/backends/jeb/DN2URI.java
@@ -445,7 +445,7 @@
          {
            urlBaseDN =
                 EntryContainer.modDN(targetDN,
                                      referralDN.getRDNComponents().length,
                                      referralDN.getNumComponents(),
                                      ldapurl.getBaseDN());
          }
          ldapurl.setBaseDN(urlBaseDN);
@@ -616,8 +616,8 @@
          // Make sure the referral is within scope.
          if (searchOp.getScope() == SearchScope.SINGLE_LEVEL)
          {
            if ((dn.getRDNComponents().length !=
                 baseDN.getRDNComponents().length + 1))
            if ((dn.getNumComponents() !=
                 baseDN.getNumComponents() + 1))
            {
              continue;
            }
opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -53,7 +53,6 @@
import org.opends.server.types.Entry;
import org.opends.server.types.LockType;
import org.opends.server.types.Modification;
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.SearchScope;
import org.opends.server.util.StaticUtils;
@@ -903,8 +902,8 @@
          if (searchScope == SearchScope.SINGLE_LEVEL)
          {
            // Check if this entry is an immediate child.
            if ((dn.getRDNComponents().length !=
                 baseDN.getRDNComponents().length + 1))
            if ((dn.getNumComponents() !=
                 baseDN.getNumComponents() + 1))
            {
              isInScope = false;
            }
@@ -1131,8 +1130,8 @@
            else if (searchScope == SearchScope.SINGLE_LEVEL)
            {
              // Check if this entry is an immediate child.
              if ((entryDN.getRDNComponents().length ==
                   baseDN.getRDNComponents().length + 1) &&
              if ((entryDN.getNumComponents() ==
                   baseDN.getNumComponents() + 1) &&
                   entryDN.isDescendantOf(baseDN))
              {
                isInScope = true;
@@ -1147,8 +1146,8 @@
            }
            else if (searchScope == SearchScope.SUBORDINATE_SUBTREE)
            {
              if ((entryDN.getRDNComponents().length >
                   baseDN.getRDNComponents().length) &&
              if ((entryDN.getNumComponents() >
                   baseDN.getNumComponents()) &&
                   entryDN.isDescendantOf(baseDN))
              {
                isInScope = true;
@@ -2702,7 +2701,7 @@
          // Construct the new DN of the entry.
          DN newDN = modDN(oldEntry.getDN(),
                           oldApexDN.getRDNComponents().length,
                           oldApexDN.getNumComponents(),
                           newApexEntry.getDN());
          if (requestedNewSuperiorDN != null)
@@ -3078,23 +3077,8 @@
   */
  public static DN modDN(DN oldDN, int oldSuffixLen, DN newSuffixDN)
  {
    RDN[] oldRDNs = oldDN.getRDNComponents();
    RDN[] suffixRDNs = newSuffixDN.getRDNComponents();
    int prefixLen = oldRDNs.length - oldSuffixLen;
    RDN[] newRDNs = new RDN[prefixLen + suffixRDNs.length];
    // Copy the unchanged prefix.
    System.arraycopy(oldRDNs, 0,
                     newRDNs, 0,
                     prefixLen);
    // Copy the new suffix.
    System.arraycopy(suffixRDNs, 0,
                     newRDNs, prefixLen,
                     suffixRDNs.length);
    return new DN(newRDNs);
    DN localName = oldDN.getLocalName(oldSuffixLen);
    return newSuffixDN.concat(localName);
  }
  /**
opends/src/server/org/opends/server/backends/jeb/ImportJob.java
@@ -705,7 +705,7 @@
        }
        else
        {
          IDs = new ArrayList<EntryID>(entryDN.getRDNComponents().length);
          IDs = new ArrayList<EntryID>(entryDN.getNumComponents());
          IDs.add(entryID);
          if (parentID != null)
          {
@@ -838,7 +838,7 @@
      importContext = importMap.get(nodeDN);
      if (importContext == null)
      {
        nodeDN = nodeDN.getParent();
        nodeDN = nodeDN.getParentDNInSuffix();
      }
    }
opends/src/server/org/opends/server/backends/jeb/RootContainer.java
@@ -524,7 +524,7 @@
      ec = entryContainers.get(nodeDN);
      if (ec == null)
      {
        nodeDN = nodeDN.getParent();
        nodeDN = nodeDN.getParentDNInSuffix();
      }
    }
opends/src/server/org/opends/server/backends/jeb/VerifyJob.java
@@ -734,8 +734,8 @@
            }
            if (!childEntry.getDN().isDescendantOf(entry.getDN()) ||
                 childEntry.getDN().getRDNComponents().length !=
                 entry.getDN().getRDNComponents().length + 1)
                 childEntry.getDN().getNumComponents() !=
                 entry.getDN().getNumComponents() + 1)
            {
              errorCount++;
              System.err.printf("File id2children has ID %d with DN <%s> " +
@@ -1512,7 +1512,7 @@
    {
      return null;
    }
    return dn.getParent();
    return dn.getParentDNInSuffix();
  }
  /**
opends/src/server/org/opends/server/backends/task/TaskBackend.java
@@ -453,7 +453,7 @@
      return taskScheduler.getRecurringTaskParentEntry();
    }
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      return null;
@@ -509,7 +509,7 @@
      return true;
    }
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      return false;
@@ -553,7 +553,7 @@
    // Get the DN for the entry and then get its parent.
    DN entryDN = entry.getDN();
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
@@ -618,7 +618,7 @@
    // Get the parent for the provided entry DN.  It must be either the
    // scheduled or recurring task parent DN.
    DN parentDN = entryDN.getParent();
    DN parentDN = entryDN.getParentDNInSuffix();
    if (parentDN == null)
    {
      int    msgID   = MSGID_TASKBE_DELETE_INVALID_ENTRY;
@@ -841,7 +841,7 @@
    }
    else
    {
      DN parentDN = baseDN.getParent();
      DN parentDN = baseDN.getParentDNInSuffix();
      if (parentDN == null)
      {
        int    msgID   = MSGID_TASKBE_SEARCH_INVALID_BASE;
opends/src/server/org/opends/server/backends/task/TaskScheduler.java
@@ -1009,7 +1009,7 @@
        }
        else
        {
          DN parentDN = entryDN.getParent();
          DN parentDN = entryDN.getParentDNInSuffix();
          if (parentDN == null)
          {
            int    msgID   = MSGID_TASKSCHED_ENTRY_HAS_NO_PARENT;
opends/src/server/org/opends/server/controls/ProxiedAuthV2Control.java
@@ -266,7 +266,7 @@
    // Check for a zero-length value, which would be for an anonymous user.
    if (authorizationID.value().length == 0)
    {
      return new DN();
      return DN.nullDN();
    }
@@ -346,7 +346,7 @@
      // If the authorization ID is just "u:", then it's an anonymous request.
      if (lowerAuthzID.length() == 2)
      {
        return new DN();
        return DN.nullDN();
      }
opends/src/server/org/opends/server/core/AddOperation.java
@@ -1014,11 +1014,11 @@
      Lock parentLock = null;
      Lock entryLock  = null;
      DN parentDN = entryDN.getParent();
      DN parentDN = entryDN.getParentDNInSuffix();
      if (parentDN == null)
      {
        // Either this entry is a suffix or doesn't belong in the directory.
        if (entryDN.isSuffix())
        if (DirectoryServer.isSuffix(entryDN))
        {
          // This is fine.  This entry is one of the configured suffixes.
          parentLock = null;
@@ -1166,7 +1166,7 @@
            if (parentEntry == null)
            {
              DN matchedDN = parentDN.getParent();
              DN matchedDN = parentDN.getParentDNInSuffix();
              while (matchedDN != null)
              {
                try
@@ -1183,7 +1183,7 @@
                  break;
                }
                matchedDN = matchedDN.getParent();
                matchedDN = matchedDN.getParentDNInSuffix();
              }
@@ -1211,15 +1211,12 @@
        // Check to make sure that all of the RDN attributes are included as
        // attribute values.  If not, then either add them or report an error.
        RDN rdn = entryDN.getRDN();
        AttributeType[]  rdnTypes  = rdn.getAttributeTypes();
        AttributeValue[] rdnValues = rdn.getAttributeValues();
        String[]         rdnNames  = rdn.getAttributeNames();
        for (int i=0; i < rdnTypes.length; i++)
        int numAVAs = rdn.getNumValues();
        for (int i=0; i < numAVAs; i++)
        {
          AttributeType  t = rdnTypes[i];
          AttributeValue v = rdnValues[i];
          AttributeType  t = rdn.getAttributeType(i);
          AttributeValue v = rdn.getAttributeValue(i);
          String         n = rdn.getAttributeName(i);
          if (t.isOperational())
          {
            List<Attribute> attrList = operationalAttributes.get(t);
@@ -1233,7 +1230,7 @@
                valueList.add(v);
                attrList = new ArrayList<Attribute>();
                attrList.add(new Attribute(t, rdnNames[i], valueList));
                attrList.add(new Attribute(t, n, valueList));
                operationalAttributes.put(t, attrList);
              }
@@ -1243,7 +1240,7 @@
                int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                              rdnNames[i]));
                                              n));
                break addProcessing;
              }
@@ -1277,7 +1274,7 @@
                  LinkedHashSet<AttributeValue> valueList =
                       new LinkedHashSet<AttributeValue>(1);
                  valueList.add(v);
                  attrList.add(new Attribute(t, rdnNames[i], valueList));
                  attrList.add(new Attribute(t, n, valueList));
                }
                else
                {
@@ -1285,7 +1282,7 @@
                  int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE;
                  appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                                rdnNames[i]));
                                                n));
                  break addProcessing;
                }
@@ -1305,7 +1302,7 @@
                valueList.add(v);
                attrList = new ArrayList<Attribute>();
                attrList.add(new Attribute(t, rdnNames[i], valueList));
                attrList.add(new Attribute(t, n, valueList));
                userAttributes.put(t, attrList);
              }
@@ -1315,7 +1312,7 @@
                int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE;
                appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                              rdnNames[i]));
                                              n));
                break addProcessing;
              }
@@ -1349,7 +1346,7 @@
                  LinkedHashSet<AttributeValue> valueList =
                       new LinkedHashSet<AttributeValue>(1);
                  valueList.add(v);
                  attrList.add(new Attribute(t, rdnNames[i], valueList));
                  attrList.add(new Attribute(t, n, valueList));
                }
                else
                {
@@ -1357,7 +1354,7 @@
                  int msgID = MSGID_ADD_MISSING_RDN_ATTRIBUTE;
                  appendErrorMessage(getMessage(msgID, String.valueOf(entryDN),
                                                rdnNames[i]));
                                                n));
                  break addProcessing;
                }
opends/src/server/org/opends/server/core/CertificateMapperConfigManager.java
@@ -146,7 +146,8 @@
      try
      {
        ConfigEntry parentEntry =
             DirectoryServer.getConfigEntry(configEntryDN.getParent());
             DirectoryServer
            .getConfigEntry(configEntryDN.getParentDNInSuffix());
        if (parentEntry != null)
        {
          parentEntry.registerAddListener(this);
@@ -172,7 +173,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntryDN.getParent();
      DN parentDN = configEntryDN.getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
@@ -860,7 +861,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntry.getDN().getParent();
      DN parentDN = configEntry.getDN().getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
opends/src/server/org/opends/server/core/CompareOperation.java
@@ -697,7 +697,7 @@
                                          String.valueOf(entryDN)));
            // See if one of the entry's ancestors exists.
            DN parentDN = entryDN.getParent();
            DN parentDN = entryDN.getParentDNInSuffix();
            while (parentDN != null)
            {
              try
@@ -714,7 +714,7 @@
                break;
              }
              parentDN = parentDN.getParent();
              parentDN = parentDN.getParentDNInSuffix();
            }
            break compareProcessing;
opends/src/server/org/opends/server/core/DeleteOperation.java
@@ -617,7 +617,7 @@
            try
            {
              DN parentDN = entryDN.getParent();
              DN parentDN = entryDN.getParentDNInSuffix();
              while (parentDN != null)
              {
                if (DirectoryServer.entryExists(parentDN))
@@ -626,7 +626,7 @@
                  break;
                }
                parentDN = parentDN.getParent();
                parentDN = parentDN.getParentDNInSuffix();
              }
            }
            catch (Exception e)
opends/src/server/org/opends/server/core/DirectoryServer.java
@@ -31,7 +31,6 @@
import java.io.FileOutputStream;
import java.io.PrintStream;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
@@ -1926,7 +1925,7 @@
      throw new InitializationException(msgID, message, e);
    }
    DN[] baseDNs   = { new DN(new RDN[0]) };
    DN[] baseDNs   = { DN.nullDN() };
    rootDSEBackend = new RootDSEBackend();
    rootDSEBackend.initializeBackend(rootDSEConfigEntry, baseDNs);
  }
@@ -5372,7 +5371,7 @@
    while (backend == null)
    {
      dn = dn.getParent();
      dn = dn.getParentDNInSuffix();
      if (dn == null)
      {
        break;
@@ -5422,7 +5421,7 @@
      }
      boolean found = false;
      DN parentDN = suffixDN.getParent();
      DN parentDN = suffixDN.getParentDNInSuffix();
      while (parentDN != null)
      {
        b = directoryServer.suffixes.get(suffixDN);
@@ -5444,7 +5443,7 @@
          }
        }
        parentDN = parentDN.getParent();
        parentDN = parentDN.getParentDNInSuffix();
      }
@@ -5513,7 +5512,7 @@
          throw new ConfigException(msgID, message);
        }
        DN parentDN = suffixDN.getParent();
        DN parentDN = suffixDN.getParentDNInSuffix();
        while (parentDN != null)
        {
          b = directoryServer.suffixes.get(suffixDN);
@@ -5525,7 +5524,7 @@
            throw new ConfigException(msgID, message);
          }
          parentDN = suffixDN.getParent();
          parentDN = suffixDN.getParentDNInSuffix();
        }
      }
@@ -5542,7 +5541,7 @@
        throw new ConfigException(msgID, message);
      }
      DN parentDN = suffixDN.getParent();
      DN parentDN = suffixDN.getParentDNInSuffix();
      while (parentDN != null)
      {
        b = directoryServer.privateSuffixes.get(suffixDN);
@@ -5563,7 +5562,7 @@
          }
        }
        parentDN = suffixDN.getParent();
        parentDN = suffixDN.getParentDNInSuffix();
      }
@@ -5599,7 +5598,7 @@
        return;
      }
      DN parentDN = suffixDN.getParent();
      DN parentDN = suffixDN.getParentDNInSuffix();
      while (parentDN != null)
      {
        b = directoryServer.suffixes.get(parentDN);
@@ -5625,7 +5624,7 @@
        return;
      }
      DN parentDN = suffixDN.getParent();
      DN parentDN = suffixDN.getParentDNInSuffix();
      while (parentDN != null)
      {
        b = directoryServer.privateSuffixes.get(parentDN);
@@ -7328,7 +7327,7 @@
      {
        // The config handler hasn't been initialized yet.  Just return the DN
        // of the root DSE.
        return new DN(new ArrayList<RDN>(0));
        return DN.nullDN();
      }
      return configHandler.getConfigRootEntry().getDN();
@@ -7339,7 +7338,7 @@
      // This could theoretically happen if an alert needs to be sent before the
      // configuration is initialized.  In that case, just return an empty DN.
      return new DN(new ArrayList<RDN>(0));
      return DN.nullDN();
    }
  }
@@ -7782,11 +7781,9 @@
      // Cannot reach this point.
      throw new RuntimeException();
    }
    RDN[] baseRDNs = monitorRootDN.getRDNComponents();
    RDN[] rdns = new RDN[baseRDNs.length+1];
    rdns[0] = new RDN(cnType, new AttributeValue(cnType, monitorName));
    System.arraycopy(baseRDNs, 0, rdns, 1, baseRDNs.length);
    return new DN(rdns);
    RDN rdn = RDN.create(cnType, new AttributeValue(cnType, monitorName));
    return monitorRootDN.concat(rdn);
  }
}
opends/src/server/org/opends/server/core/EntryCacheConfigManager.java
@@ -141,8 +141,8 @@
      try
      {
        ConfigEntry parentEntry =
             DirectoryServer.getConfigEntry(configEntryDN.getParent());
        ConfigEntry parentEntry = DirectoryServer
            .getConfigEntry(configEntryDN.getParentDNInSuffix());
        if (parentEntry != null)
        {
          parentEntry.registerAddListener(this);
@@ -168,7 +168,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntryDN.getParent();
      DN parentDN = configEntryDN.getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
opends/src/server/org/opends/server/core/KeyManagerProviderConfigManager.java
@@ -143,8 +143,8 @@
      try
      {
        ConfigEntry parentEntry =
             DirectoryServer.getConfigEntry(configEntryDN.getParent());
        ConfigEntry parentEntry = DirectoryServer
            .getConfigEntry(configEntryDN.getParentDNInSuffix());
        if (parentEntry != null)
        {
          parentEntry.registerAddListener(this);
@@ -170,7 +170,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntryDN.getParent();
      DN parentDN = configEntryDN.getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
@@ -857,7 +857,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntry.getDN().getParent();
      DN parentDN = configEntry.getDN().getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
opends/src/server/org/opends/server/core/ModifyDNOperation.java
@@ -927,7 +927,7 @@
      DN parentDN;
      if (newSuperior == null)
      {
        parentDN = entryDN.getParent();
        parentDN = entryDN.getParentDNInSuffix();
      }
      else
      {
@@ -942,14 +942,7 @@
        break modifyDNProcessing;
      }
      RDN[] parentComponents = parentDN.getRDNComponents();
      RDN[] newComponents    = new RDN[parentComponents.length+1];
      System.arraycopy(parentComponents, 0, newComponents, 1,
                       parentComponents.length);
      newComponents[0] = newRDN;
      DN newDN = new DN(newComponents);
      DN newDN = parentDN.concat(newRDN);
      // Get the backend for the current entry, and the backend for the new
      // entry.  If either is null, or if they are different, then fail.
@@ -1092,7 +1085,7 @@
        if (currentEntry == null)
        {
          // See if one of the entry's ancestors exists.
          parentDN = entryDN.getParent();
          parentDN = entryDN.getParentDNInSuffix();
          while (parentDN != null)
          {
            try
@@ -1109,7 +1102,7 @@
              break;
            }
            parentDN = parentDN.getParent();
            parentDN = parentDN.getParentDNInSuffix();
          }
          setResultCode(ResultCode.NO_SUCH_OBJECT);
@@ -1416,10 +1409,10 @@
          {
            LinkedHashSet<AttributeValue> valueSet =
                 new LinkedHashSet<AttributeValue>(1);
            valueSet.add(currentRDN.getAttributeValues()[i]);
            valueSet.add(currentRDN.getAttributeValue(i));
            Attribute a = new Attribute(currentRDN.getAttributeTypes()[i],
                                        currentRDN.getAttributeNames()[i],
            Attribute a = new Attribute(currentRDN.getAttributeType(i),
                                        currentRDN.getAttributeName(i),
                                        valueSet);
            // If the associated attribute type is marked NO-USER-MODIFICATION,
@@ -1455,10 +1448,10 @@
        {
          LinkedHashSet<AttributeValue> valueSet =
               new LinkedHashSet<AttributeValue>(1);
          valueSet.add(newRDN.getAttributeValues()[i]);
          valueSet.add(newRDN.getAttributeValue(i));
          Attribute a = new Attribute(newRDN.getAttributeTypes()[i],
                                      newRDN.getAttributeNames()[i],
          Attribute a = new Attribute(newRDN.getAttributeType(i),
                                      newRDN.getAttributeName(i),
                                      valueSet);
          LinkedList<AttributeValue> duplicateValues =
opends/src/server/org/opends/server/core/ModifyOperation.java
@@ -923,7 +923,7 @@
                                        String.valueOf(entryDN)));
          // See if one of the entry's ancestors exists.
          DN parentDN = entryDN.getParent();
          DN parentDN = entryDN.getParentDNInSuffix();
          while (parentDN != null)
          {
            try
@@ -940,7 +940,7 @@
              break;
            }
            parentDN = parentDN.getParent();
            parentDN = parentDN.getParentDNInSuffix();
          }
          break modifyProcessing;
opends/src/server/org/opends/server/core/Operation.java
@@ -42,7 +42,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.OperationType;
import org.opends.server.types.RDN;
import org.opends.server.types.ResultCode;
import org.opends.server.types.operation.PostOperationOperation;
import org.opends.server.types.operation.PostResponseOperation;
@@ -752,7 +751,7 @@
      AuthenticationInfo authInfo = clientConnection.getAuthenticationInfo();
      if (authInfo == null)
      {
        return new DN(new RDN[0]);
        return DN.nullDN();
      }
      else
      {
opends/src/server/org/opends/server/core/PersistentSearch.java
@@ -221,7 +221,7 @@
        }
        break;
      case SINGLE_LEVEL:
        if (! baseDN.equals(entry.getDN().getParent()))
        if (! baseDN.equals(entry.getDN().getParentDNInSuffix()))
        {
          return;
        }
@@ -331,7 +331,7 @@
        }
        break;
      case SINGLE_LEVEL:
        if (! baseDN.equals(entry.getDN().getParent()))
        if (! baseDN.equals(entry.getDN().getParentDNInSuffix()))
        {
          return;
        }
@@ -444,7 +444,7 @@
        }
        break;
      case SINGLE_LEVEL:
        if (! baseDN.equals(oldEntry.getDN().getParent()))
        if (! baseDN.equals(oldEntry.getDN().getParentDNInSuffix()))
        {
          return;
        }
@@ -567,8 +567,8 @@
        break;
      case SINGLE_LEVEL:
        oldMatches = baseDN.equals(oldEntry.getDN().getParent());
        newMatches = baseDN.equals(newEntry.getDN().getParent());
        oldMatches = baseDN.equals(oldEntry.getDN().getParentDNInSuffix());
        newMatches = baseDN.equals(newEntry.getDN().getParentDNInSuffix());
        if (! (oldMatches || newMatches))
        {
opends/src/server/org/opends/server/core/RFC3672SubtreeSpecification.java
@@ -689,7 +689,7 @@
  public RFC3672SubtreeSpecification(DN rootDN, DN relativeBaseDN,
      int minimumDepth, int maximumDepth, Iterable<DN> chopBefore,
      Iterable<DN> chopAfter, Refinement refinements) {
    super(relativeBaseDN == null ? rootDN : new DN(rootDN, relativeBaseDN),
    super(relativeBaseDN == null ? rootDN : rootDN.concat(relativeBaseDN),
        minimumDepth, maximumDepth, chopBefore, chopAfter);
    assert debugConstructor(CLASS_NAME);
opends/src/server/org/opends/server/core/RelativeSubtreeSpecification.java
@@ -248,7 +248,7 @@
  public RelativeSubtreeSpecification(DN rootDN, DN relativeBaseDN,
      int minimumDepth, int maximumDepth, Iterable<DN> chopBefore,
      Iterable<DN> chopAfter, SearchFilter filter) {
    super(relativeBaseDN == null ? rootDN : new DN(rootDN, relativeBaseDN),
    super(relativeBaseDN == null ? rootDN : rootDN.concat(relativeBaseDN),
        minimumDepth, maximumDepth, chopBefore, chopAfter);
    assert debugConstructor(CLASS_NAME);
opends/src/server/org/opends/server/core/SimpleSubtreeSpecification.java
@@ -390,7 +390,7 @@
      this.chopBefore = new TreeMap<DN, DN>();
      for (DN localName : chopBefore) {
        this.chopBefore.put(new DN(baseDN, localName), localName);
        this.chopBefore.put(baseDN.concat(localName), localName);
      }
    } else {
      // No chop before specifications.
@@ -402,7 +402,7 @@
      this.chopAfter = new TreeMap<DN, DN>();
      for (DN localName : chopAfter) {
        this.chopAfter.put(new DN(baseDN, localName), localName);
        this.chopAfter.put(baseDN.concat(localName), localName);
      }
    } else {
      // No chop after specifications.
@@ -428,10 +428,10 @@
    }
    // Check minimum and maximum depths.
    int baseRDNCount = baseDN.getRDNComponents().length;
    int baseRDNCount = baseDN.getNumComponents();
    if (minimumDepth > 0) {
      int entryRDNCount = dn.getRDNComponents().length;
      int entryRDNCount = dn.getNumComponents();
      if (entryRDNCount - baseRDNCount < minimumDepth) {
        return false;
@@ -439,7 +439,7 @@
    }
    if (maximumDepth >= 0) {
      int entryRDNCount = dn.getRDNComponents().length;
      int entryRDNCount = dn.getNumComponents();
      if (entryRDNCount - baseRDNCount > maximumDepth) {
        return false;
opends/src/server/org/opends/server/core/TrustManagerProviderConfigManager.java
@@ -144,8 +144,8 @@
      try
      {
        ConfigEntry parentEntry =
             DirectoryServer.getConfigEntry(configEntryDN.getParent());
        ConfigEntry parentEntry = DirectoryServer
            .getConfigEntry(configEntryDN.getParentDNInSuffix());
        if (parentEntry != null)
        {
          parentEntry.registerAddListener(this);
@@ -171,7 +171,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntryDN.getParent();
      DN parentDN = configEntryDN.getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
@@ -861,7 +861,7 @@
    configEntry.registerChangeListener(this);
    try
    {
      DN parentDN = configEntry.getDN().getParent();
      DN parentDN = configEntry.getDN().getParentDNInSuffix();
      ConfigEntry parentEntry = DirectoryServer.getConfigEntry(parentDN);
      if (parentEntry != null)
      {
opends/src/server/org/opends/server/extensions/PasswordModifyExtendedOperation.java
@@ -1015,7 +1015,7 @@
                                                String.valueOf(entryDN)));
        // See if one of the entry's ancestors exists.
        DN parentDN = entryDN.getParent();
        DN parentDN = entryDN.getParentDNInSuffix();
        while (parentDN != null)
        {
          try
@@ -1032,7 +1032,7 @@
            break;
          }
          parentDN = parentDN.getParent();
          parentDN = parentDN.getParentDNInSuffix();
        }
        return null;
opends/src/server/org/opends/server/messages/CoreMessages.java
@@ -1754,10 +1754,11 @@
  /**
   * The message ID for the string that will be used if an attempt is made to
   * decode a string as an RDN but that string contained an unexpected comma or
   * semicolon.  This takes two arguments, which are the RDN string to decode
   * and the position of the illegal comma or semicolon.
   * The message ID for the string that will be used if an attempt is
   * made to decode a string as an RDN but that string contained an
   * unexpected plus, comma, or semicolon. This takes two arguments,
   * which are the RDN string to decode and the position of the
   * illegal plus, comma, or semicolon.
   */
  public static final int MSGID_RDN_UNEXPECTED_COMMA =
       CATEGORY_MASK_CORE | SEVERITY_MASK_MILD_ERROR | 174;
@@ -6572,8 +6573,8 @@
    registerMessage(MSGID_RDN_UNEXPECTED_COMMA,
                    "Unable to decode the provided string \"%s\" as a " +
                    "relative distinguished name because it contained an " +
                    "unexpected comma or semicolon at position %d, which is " +
                    "not allowed in an RDN.");
                    "unexpected plus, comma, or semicolon at position %d, "+
                    "which is not allowed in an RDN.");
    registerMessage(MSGID_RDN_ILLEGAL_CHARACTER,
                    "Unable to decode the provided string \"%s\" as a " +
                    "relative distinguished name because an illegal " +
opends/src/server/org/opends/server/protocols/jmx/JmxConnectionHandler.java
@@ -337,7 +337,7 @@
    // in the current entry.
    // Always return true as the check will be performed by the
    // hasAcceptableConfiguration call
    if (configEntry.getDN().compareTo(configEntryDN) == 0)
    if (configEntry.getDN().equals(configEntryDN))
    {
      return true;
    }
@@ -370,7 +370,7 @@
    //
    // We are checking first if we are dealing with a change
    // in the current entry.
    if (configEntry.getDN().compareTo(configEntryDN) == 0)
    if (configEntry.getDN().equals(configEntryDN))
    {
      ArrayList<String> messages = new ArrayList<String>();
      return new ConfigChangeResult(ResultCode.SUCCESS, false, messages);
opends/src/server/org/opends/server/schema/OctetStringOrderingMatchingRule.java
@@ -35,12 +35,10 @@
import org.opends.server.types.ByteString;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.InitializationException;
import org.opends.server.util.StaticUtils;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.schema.SchemaConstants.*;
import static org.opends.server.util.StaticUtils.*;
@@ -229,36 +227,7 @@
    assert debugEnter(CLASS_NAME, "compareValues", String.valueOf(b1),
                      String.valueOf(b2));
    int minLength = Math.min(b1.length, b2.length);
    for (int i=0; i < minLength; i++)
    {
      if (b1[i] == b2[i])
      {
        continue;
      }
      else if (b1[i] < b2[i])
      {
        return -1;
      }
      else if (b1[i] > b2[i])
      {
        return 1;
      }
    }
    if (b1.length == b2.length)
    {
      return 0;
    }
    else if (b1.length < b2.length)
    {
      return -1;
    }
    else
    {
      return 1;
    }
    return StaticUtils.compare(b1, b2);
  }
}
opends/src/server/org/opends/server/schema/RFC3672SubtreeSpecificationSyntax.java
@@ -277,7 +277,7 @@
    // Use the subtree specification code to make this determination.
    try {
      RFC3672SubtreeSpecification.valueOf(new DN(), value.stringValue());
      RFC3672SubtreeSpecification.valueOf(DN.nullDN(), value.stringValue());
      return true;
    } catch (DirectoryException e) {
opends/src/server/org/opends/server/schema/RelativeSubtreeSpecificationSyntax.java
@@ -278,7 +278,7 @@
    // Use the subtree specification code to make this determination.
    try {
      RelativeSubtreeSpecification.valueOf(new DN(), value.stringValue());
      RelativeSubtreeSpecification.valueOf(DN.nullDN(), value.stringValue());
      return true;
    } catch (DirectoryException e) {
opends/src/server/org/opends/server/synchronization/MultimasterSynchronization.java
@@ -462,7 +462,7 @@
    do
    {
      domain = domains.get(temp);
      temp = temp.getParent();
      temp = temp.getParentDNInSuffix();
      if (temp == null)
      {
        break;
opends/src/server/org/opends/server/synchronization/SynchronizationDomain.java
@@ -662,7 +662,7 @@
  {
    AddContext ctx = new AddContext(generateChangeNumber(addOperation),
        Historical.getEntryUuid(addOperation),
        findEntryId(addOperation.getEntryDN().getParent()));
        findEntryId(addOperation.getEntryDN().getParentDNInSuffix()));
    addOperation.setAttachment(SYNCHROCONTEXT, ctx);
  }
@@ -1491,13 +1491,7 @@
      throw new Exception("operation parameters are invalid");
    }
    RDN[] parentComponents = parentDN.getRDNComponents();
    RDN[] newComponents    = new RDN[parentComponents.length+1];
    System.arraycopy(parentComponents, 0, newComponents, 1,
        parentComponents.length);
    newComponents[0] = newRDN;
    DN newDN = new DN(newComponents);
    DN newDN = parentDN.concat(newRDN);
    // get the current DN of this entry in the database.
    DN currentDN = findEntryDN(entryUid);
opends/src/server/org/opends/server/tools/LDIFDiff.java
@@ -42,7 +42,6 @@
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DN;
import org.opends.server.types.DNComparator;
import org.opends.server.types.Entry;
import org.opends.server.types.ExistingFileBehavior;
import org.opends.server.types.LDIFImportConfig;
@@ -277,8 +276,7 @@
      return 1;
    }
    DNComparator comparator = new DNComparator();
    TreeMap<DN,Entry> sourceMap = new TreeMap<DN,Entry>(comparator);
    TreeMap<DN,Entry> sourceMap = new TreeMap<DN,Entry>();
    try
    {
      while (true)
@@ -324,7 +322,7 @@
      return 1;
    }
    TreeMap<DN,Entry> targetMap = new TreeMap<DN,Entry>(comparator);
    TreeMap<DN,Entry> targetMap = new TreeMap<DN,Entry>();
    try
    {
      while (true)
@@ -437,9 +435,9 @@
        while (true)
        {
          // Use the DN comparator to determine the relative order of the
          // Compare the DNs to determine the relative order of the
          // entries.
          int comparatorValue = comparator.compare(sourceDN, targetDN);
          int comparatorValue = sourceDN.compareTo(targetDN);
          if (comparatorValue < 0)
          {
            // The source entry should be before the target entry, which means
opends/src/server/org/opends/server/tools/LDIFModify.java
@@ -45,7 +45,6 @@
import org.opends.server.types.Attribute;
import org.opends.server.types.AttributeType;
import org.opends.server.types.AttributeValue;
import org.opends.server.types.DNComparator;
import org.opends.server.types.DirectoryException;
import org.opends.server.types.DN;
import org.opends.server.types.Entry;
@@ -126,11 +125,10 @@
         throws IOException, LDIFException
  {
    // Read the changes into memory.
      DNComparator comparator = new DNComparator();
      TreeMap<DN,AddChangeRecordEntry> adds =
          new TreeMap<DN,AddChangeRecordEntry>(comparator);
          new TreeMap<DN,AddChangeRecordEntry>();
      TreeMap<DN,Entry> ldifEntries =
          new TreeMap<DN,Entry>(comparator);
          new TreeMap<DN,Entry>();
    HashMap<DN,DeleteChangeRecordEntry> deletes =
         new HashMap<DN,DeleteChangeRecordEntry>();
    HashMap<DN,LinkedList<Modification>> modifications =
opends/src/server/org/opends/server/tools/LDIFSearch.java
@@ -521,7 +521,7 @@
    }
    else
    {
      baseDNs.add(new DN());
      baseDNs.add(DN.nullDN());
    }
opends/src/server/org/opends/server/tools/makeldif/DNTag.java
@@ -32,7 +32,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.types.RDN;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ToolMessages.*;
@@ -205,26 +204,25 @@
    }
    else if (numComponents > 0)
    {
      RDN[] rdnComps = dn.getRDNComponents();
      int count = Math.min(numComponents, rdnComps.length);
      int count = Math.min(numComponents, dn.getNumComponents());
      rdnComps[0].toString(templateValue.getValue());
      dn.getRDN(0).toString(templateValue.getValue());
      for (int i=1; i < count; i++)
      {
        templateValue.append(",");
        rdnComps[i].toString(templateValue.getValue());
        dn.getRDN(i).toString(templateValue.getValue());
      }
    }
    else
    {
      RDN[] rdnComps = dn.getRDNComponents();
      int count = Math.min(Math.abs(numComponents), rdnComps.length);
      int sz = dn.getNumComponents();
      int count = Math.min(Math.abs(numComponents), sz);
      rdnComps[rdnComps.length-count].toString(templateValue.getValue());
      dn.getRDN(sz - count).toString(templateValue.getValue());
      for (int i=1; i < count; i++)
      {
        templateValue.append(",");
        rdnComps[rdnComps.length-count+i].toString(templateValue.getValue());
        dn.getRDN(sz - count + i).toString(templateValue.getValue());
      }
    }
opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java
@@ -155,33 +155,22 @@
   */
  public DN getDN()
  {
    if (dn == null)
    {
      AttributeType[]  rdnAttributes = template.getRDNAttributes();
      String[]         rdnNames      = new String[rdnAttributes.length];
      AttributeValue[] rdnValues     = new AttributeValue[rdnAttributes.length];
    if (dn == null) {
      RDN.Builder builder = RDN.createBuilder();
      for (int i=0; i < rdnAttributes.length; i++)
      {
        TemplateValue v = getValue(rdnAttributes[i]);
        if (v == null)
        {
      for (AttributeType type : template.getRDNAttributes()) {
        TemplateValue v = getValue(type);
        if (v == null) {
          return null;
        }
        rdnNames[i]  = rdnAttributes[i].getNameOrOID();
        rdnValues[i] = new AttributeValue(rdnAttributes[i],
        AttributeValue value = new AttributeValue(type,
                                          v.getValue().toString());
        builder.append(type, value);
      }
      RDN[] parentComponents = parentDN.getRDNComponents();
      RDN[] dnComponents = new RDN[parentComponents.length+1];
      dnComponents[0] = new RDN(rdnAttributes, rdnNames, rdnValues);
      System.arraycopy(parentComponents, 0, dnComponents, 1,
                       parentComponents.length);
      dn = new DN(dnComponents);
      dn = parentDN.concat(builder.getInstance());
    }
    return dn;
opends/src/server/org/opends/server/tools/makeldif/UnderscoreDNTag.java
@@ -32,7 +32,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.types.RDN;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ToolMessages.*;
@@ -201,37 +200,33 @@
    if (numComponents == 0)
    {
      RDN[] rdnComps = dn.getRDNComponents();
      rdnComps[0].toString(templateValue.getValue());
      for (int i=1; i < rdnComps.length; i++)
      dn.getRDN(0).toString(templateValue.getValue());
      for (int i=1; i < dn.getNumComponents(); i++)
      {
        templateValue.append("_");
        rdnComps[i].toString(templateValue.getValue());
        dn.getRDN(i).toString(templateValue.getValue());
      }
    }
    else if (numComponents > 0)
    {
      RDN[] rdnComps = dn.getRDNComponents();
      int count = Math.min(numComponents, rdnComps.length);
      int count = Math.min(numComponents, dn.getNumComponents());
      rdnComps[0].toString(templateValue.getValue());
      dn.getRDN(0).toString(templateValue.getValue());
      for (int i=1; i < count; i++)
      {
        templateValue.append("_");
        rdnComps[i].toString(templateValue.getValue());
        dn.getRDN(i).toString(templateValue.getValue());
      }
    }
    else
    {
      RDN[] rdnComps = dn.getRDNComponents();
      int count = Math.min(Math.abs(numComponents), rdnComps.length);
      int sz = dn.getNumComponents();
      int count = Math.min(Math.abs(numComponents), sz);
      rdnComps[rdnComps.length-count].toString(templateValue.getValue());
      for (int i=1; i < count; i++)
      {
      dn.getRDN(sz - count).toString(templateValue.getValue());
      for (int i = 1; i < count; i++) {
        templateValue.append("_");
        rdnComps[rdnComps.length-count+i].toString(templateValue.getValue());
        dn.getRDN(sz - count + i).toString(templateValue.getValue());
      }
    }
opends/src/server/org/opends/server/tools/makeldif/UnderscoreParentDNTag.java
@@ -32,7 +32,6 @@
import org.opends.server.types.DN;
import org.opends.server.types.InitializationException;
import org.opends.server.types.RDN;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.ToolMessages.*;
@@ -132,12 +131,11 @@
      return TagResult.SUCCESS_RESULT;
    }
    RDN[] rdnComps = parentDN.getRDNComponents();
    rdnComps[0].toString(templateValue.getValue());
    for (int i=1; i < rdnComps.length; i++)
    parentDN.getRDN(0).toString(templateValue.getValue());
    for (int i=1; i < parentDN.getNumComponents(); i++)
    {
      templateValue.append("_");
      rdnComps[i].toString(templateValue.getValue());
      parentDN.getRDN(i).toString(templateValue.getValue());
    }
    return TagResult.SUCCESS_RESULT;
opends/src/server/org/opends/server/types/AttributeValue.java
@@ -32,7 +32,6 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.util.StaticUtils.*;
@@ -173,22 +172,6 @@
  /**
   * Retrieves a string representation of the user-defined form of
   * this attribute value in a form suitable for use in a DN.
   *
   * @return  A string representation of the user-defined form of this
   *          attribute value in a form suitable for use in a DN.
   */
  public String getDNStringValue()
  {
    assert debugEnter(CLASS_NAME, "getDNStringValue");
    return getDNValue(getStringValue());
  }
  /**
   * Retrieves the normalized form of this attribute value.
   *
   * @return  The normalized form of this attribute value.
@@ -263,105 +246,6 @@
  /**
   * Retrieves a string representation of the normalized form of this
   * attribute value in a form suitable for use in a DN.
   *
   * @return  A string representation of the normalized form of this
   *          attribute value in a form suitable for use in a DN.
   *
   * @throws  DirectoryException  If an error occurs while trying to
   *                              normalize the value (e.g., if it is
   *                              not acceptable for use with the
   *                              associated equality matching rule).
   */
  public String getNormalizedDNStringValue()
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "getNormalizedDNStringValue");
    return getDNValue(getNormalizedStringValue());
  }
  /**
   * Retrieves a version of the provided value in a form that is
   * properly escaped for use in a DN or RDN.
   *
   * @param  value  The value to be represented in a DN-safe form.
   *
   * @return  A version of the provided value in a form that is
   *          properly escaped for use in a DN or RDN.
   */
  private static String getDNValue(String value)
  {
    assert debugEnter(CLASS_NAME, "getDNValue",
                      String.valueOf(value));
    if ((value == null) || (value.length() == 0))
    {
      return "";
    }
    StringBuilder buffer = new StringBuilder(value);
    int length = buffer.length();
    for (int i=0; i < length; i++)
    {
      char c = buffer.charAt(i);
      if ((c < ' ') || (c > '~'))
      {
        buffer.deleteCharAt(i);
        length -= 1;
        for (byte b : getBytes(String.valueOf(c)))
        {
          buffer.insert(i++, "\\");
          buffer.insert(i++, byteToLowerHex(b));
          i++;
          length += 3;
        }
        i -= 1;
      }
      else
      {
        switch (buffer.charAt(i))
        {
          case ',':
          case '+':
          case '"':
          case '\\':
          case '<':
          case '>':
          case ';':
            buffer.insert(i++, '\\');
            length++;
        }
      }
    }
    char c = buffer.charAt(0);
    if ((c == ' ') || (c == '#'))
    {
      buffer.insert(0, '\\');
      length++;
    }
    if (buffer.charAt(length-1) == ' ')
    {
      buffer.insert(length-1, '\\');
      length++;
    }
    return buffer.toString();
  }
  /**
   * Determines whether this attribute value is equal to the provided
   * object.
   *
opends/src/server/org/opends/server/types/AttributeValueComparator.java
File was deleted
opends/src/server/org/opends/server/types/DN.java
@@ -28,17 +28,14 @@
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.util.Validator.ensureNotNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.messages.MessageHandler.*;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.util.StaticUtils.*;
@@ -46,51 +43,196 @@
 * This class defines a data structure for storing and interacting
 * with the distinguished names associated with entries in the
 * Directory Server.
 * <p>
 * All the methods in this class will throw a
 * <code>NullPointerException</code> when provided with
 * <code>null</code> reference parameters unless otherwise stated.
 */
public class DN
       implements Comparable<DN>, Serializable
{
public final class DN implements Comparable<DN>, Serializable {
  // FIXME: Don't store the normalized form and define equals(),
  // hashCode(), and compareTo() in terms of RDN components (which
  // cache their normalized form). This could potentially lead to less
  // memory utilization for very little performance loss.
  // FIXME: Optimize normalization for common use-cases. E.g.
  // concat(DN) can simply join the two normalized forms together.
  // Similarly, getParent() and getLocalName() can avoid
  // recalculating the normalized form and take a substring from the
  // source DN's normalized form (this requires parsing commas, but
  // could save memory).
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.types.DN";
  /**
   * The serial version identifier required to satisfy the compiler
   * because this class implements the
   * <CODE>java.io.Serializable</CODE> interface.  This value was
   * generated using the <CODE>serialver</CODE> command-line utility
   * <code>java.io.Serializable</code> interface. This value was
   * generated using the <code>serialver</code> command-line utility
   * included with the Java SDK.
   */
  private static final long serialVersionUID = 1184263456768819888L;
  // The number of RDN components that comprise this DN.
  private int numComponents;
  private final int numComponents;
  // The index of the first RDN component (furthest from the root) in
  // this DN.
  private final int offset;
  // The set of RDN components that comprise this DN, arranged with
  // the suffix as the last element.
  private RDN[] rdnComponents;
  private final RDN[] rdnComponents;
  // The string representation of this DN.
  private String dnString;
  // The cached normalized string representation of this DN.
  private final String normalizedDN;
  // The normalized string representation of this DN.
  private String normalizedDN;
  // A DN comprising of zero RDN components.
  private static final DN EMPTY_DN = new DN(new RDN[0], 0, 0);
  /**
   * Creates a new DN with no RDN components (i.e., a null DN or root
   * DSE).
   * Creates a new DN comprising of zero RDN components (a null DN or
   * root DSE).
   *
   * @return Returns a new DN comprising of zero RDN components.
   */
  public DN()
  {
    this(new RDN[0]);
  public static DN nullDN() {
    return EMPTY_DN;
  }
  /**
   * Creates a new <code>DN</code> containing the specified RDN
   * sequence.
   * <p>
   * If the argument RDN sequence is empty, then the effect is the
   * same as if {@link #nullDN()} had been called.
   *
   * @param rdns
   *          The RDN sequence that the new DN will represent.
   * @return Returns a DN that represents the specified RDN sequence
   *         onto the end of this DN.
   */
  public static DN create(RDN... rdns) {
    ensureNotNull(rdns);
    if (rdns.length == 0) {
      return nullDN();
    }
    RDN[] allRDNs = new RDN[rdns.length];
    System.arraycopy(rdns, 0, allRDNs, 0, rdns.length);
    return new DN(allRDNs, 0, rdns.length);
  }
  /**
   * Returns a <code>DN</code> object holding the value of the
   * specified <code>String</code>. The argument is interpreted as
   * representing the LDAP string representation of a DN.
   * <p>
   * This method is identical to {@link #decode(String)}.
   *
   * @param s
   *          The string to be parsed, or <code>null</code> in
   *          which case a <code>nullDN</code> will be returned.
   * @return Returns a <code>DN</code> holding the value represented
   *         by the <code>string</code> argument.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           string as a DN.
   */
  public static DN valueOf(String s) throws DirectoryException {
    return decode(s);
  }
  /**
   * Decodes the provided ASN.1 octet string as a DN.
   *
   * @param dnString
   *          The ASN.1 octet string to decode as a DN, or
   *          <code>null</code> in which case a <code>nullDN</code>
   *          will be returned.
   * @return The decoded DN.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           ASN.1 octet string as a DN.
   */
  public static DN decode(ByteString dnString)
      throws DirectoryException {
    assert debugEnter(CLASS_NAME, "decode", String.valueOf(dnString));
    // A null or empty DN is acceptable.
    if (dnString == null) {
      return nullDN();
    }
    byte[] dnBytes = dnString.value();
    int length = dnBytes.length;
    if (length == 0) {
      return nullDN();
    }
    // Use string-based decoder.
    return decode(dnString.stringValue());
  }
  /**
   * Decodes the provided string as a DN.
   *
   * @param dnString
   *          The string to decode as a DN, or <code>null</code> in
   *          which case a <code>nullDN</code> will be returned.
   * @return The decoded DN.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           string as a DN.
   */
  public static DN decode(String dnString) throws DirectoryException {
    assert debugEnter(CLASS_NAME, "decode", String.valueOf(dnString));
    // A null or empty DN is acceptable.
    if (dnString == null || dnString.length() == 0) {
      return nullDN();
    }
    // Parse the first RDN.
    int pos = 0;
    RDN.Builder builder = RDN.createBuilder();
    pos = builder.parse(dnString, pos, true);
    if (builder.isEmpty()) {
      return nullDN();
    } else {
      // Parse the remaining RDNs.
      List<RDN> rdns = new ArrayList<RDN>(10);
      rdns.add(builder.getInstance());
      while (pos >= 0) {
        // Skip the RDN separator.
        pos++;
        // Parse the next RDN.
        builder.clear();
        pos = builder.parse(dnString, pos, false);
        rdns.add(builder.getInstance());
      }
      // Parse successful - create the DN.
      int sz = rdns.size();
      return new DN(rdns.toArray(new RDN[sz]), 0, sz);
    }
  }
@@ -99,203 +241,255 @@
   * Creates a new DN with the provided set of RDNs, arranged with the
   * suffix as the last element.
   *
   * @param  rdnComponents  The set of RDN components that make up
   *                        this DN.
   * @param rdnComponents
   *          The set of RDN components that make up this DN.
   * @param offset
   *          The index of the first RDN component (furthest from the
   *          root) in this DN.
   * @param count
   *          The number of RDNs to include in this DN.
   */
  public DN(RDN[] rdnComponents)
  {
  private DN(RDN[] rdnComponents, int offset, int count) {
    assert debugConstructor(CLASS_NAME,
                            String.valueOf(rdnComponents));
        String.valueOf(rdnComponents), String.valueOf(offset));
    if (rdnComponents == null)
    {
      this.rdnComponents = new RDN[0];
    }
    else
    {
      this.rdnComponents = rdnComponents;
    }
    numComponents = this.rdnComponents.length;
    dnString      = null;
    normalizedDN  = toNormalizedString();
    this.offset = offset;
    this.numComponents = count;
    this.normalizedDN = normalize();
  }
  /**
   * Creates a new DN with the provided set of RDNs, arranged with the
   * suffix as the last element.
   *
   * @param  rdnComponents  The set of RDN components that make up
   * Concatenates the specified DN to the end of this DN.
   * <p>
   * If the argument DN is the null DN, then this DN is returned.
   * Conversely, if this DN is the null DN then the argument DN will
   * be returned. Otherwise, the returned DN will be a descendent of
   *                        this DN.
   *
   * @param localName
   *          The DN that will be concatenated to the end of this DN.
   * @return Returns a DN that represents the concatenation of the
   *         specified DN onto the end of this DN.
   */
  public DN(ArrayList<RDN> rdnComponents)
  {
    assert debugConstructor(CLASS_NAME,
                            String.valueOf(rdnComponents));
  public DN concat(DN localName) {
    ensureNotNull(localName);
    if ((rdnComponents == null) || rdnComponents.isEmpty())
    {
      this.rdnComponents = new RDN[0];
    }
    else
    {
      this.rdnComponents = new RDN[rdnComponents.size()];
      rdnComponents.toArray(this.rdnComponents);
    if (localName.isNullDN()) {
      return this;
    }
    numComponents = this.rdnComponents.length;
    dnString      = null;
    normalizedDN  = toNormalizedString();
    if (isNullDN()) {
      return localName;
    }
    RDN[] allRDNs = new RDN[numComponents + localName.numComponents];
    System.arraycopy(localName.rdnComponents, localName.offset,
        allRDNs, 0, localName.numComponents);
    System.arraycopy(rdnComponents, offset, allRDNs,
        localName.numComponents, numComponents);
    return new DN(allRDNs, 0, allRDNs.length);
  }
  /**
   * Constructs a new DN which is the concatenation of a base DN and a
   * relative DN. The constructed DN has the following property:
   * Concatenates the specified RDN sequence to the end of this DN.
   * <p>
   * If the argument RDN sequence is empty, then this DN is returned.
   * Otherwise, the returned DN will be a descendent of this DN.
   *
   * @param rdns
   *          The RDN sequence that will be concatenated to the end of
   *          this DN.
   * @return Returns a DN that represents the concatenation of the
   *         specified RDN sequence onto the end of this DN.
   */
  public DN concat(RDN... rdns) {
    ensureNotNull(rdns);
    if (rdns.length == 0) {
      return this;
    }
    // Don't check if this is a nullDN, because were going to copy the
    // RDN sequence anyway.
    RDN[] allRDNs = new RDN[rdns.length + numComponents];
    System.arraycopy(rdns, 0, allRDNs, 0, rdns.length);
    System.arraycopy(rdnComponents, offset, allRDNs,
        rdns.length, numComponents);
    return new DN(allRDNs, 0, allRDNs.length);
  }
  /**
   * Get the parent DN of this DN.
   *
   * @return Returns the parent DN of this DN, or <code>null</code>
   *         if this DN does not have a parent (i.e. it is a DN having
   *         a single RDN component, or the null DN).
   */
  public DN getParent() {
    if (numComponents <= 1) {
      return null;
    } else {
      return new DN(rdnComponents, offset + 1, numComponents - 1);
    }
  }
  /**
   * Create a local name (a relative DN) from this DN.
   * <p>
   * Examples: <blockquote>
   *
   * <pre>
   *         DN suffix;
   *         DN localName;
   * DN dn = DN.decode(&quot;cn=john,o=example,c=us&quot;);
   *
   *         suffix.isAncestorOf(new DN(suffix, localName)) == true;
   * dn.getLocalName(0) returns &quot;cn=john,o=example,c=us&quot;
   * dn.getLocalName(1) returns &quot;cn=john,o=example&quot;
   * dn.getLocalName(3) returns &quot;&quot; (null DN).
   * </pre>
   *
   * @param baseDN
   *          The base DN.
   * @param localName
   *          The relative DN.
   * </blockquote>
   *
   * @param beginIndex
   *          The index of the first RDN component (nearest the root),
   *          inclusive.
   * @return Returns the specified local name.
   * @throws IndexOutOfBoundsException
   *           If <code>beginIndex</code> is negative, or greater
   *           than the number of RDN components in this DN.
   */
  public DN(DN baseDN, DN localName)
  {
    this(baseDN, localName.rdnComponents);
  public DN getLocalName(int beginIndex)
      throws IndexOutOfBoundsException {
    return getLocalName(beginIndex, numComponents);
  }
  /**
   * Constructs a new DN which is the concatenation of a base DN and
   * an RDN sequence.
   * Create a local name (a relative DN) from this DN.
   * <p>
   * Examples: <blockquote>
   *
   * @param  baseDN       The base DN.
   * @param  rdnSequence  The RDN sequence which will become the
   *                      outermost RDNs of the concatenated DN.
   * <pre>
   * DN dn = DN.decode(&quot;cn=john,o=example,c=us&quot;);
   *
   * dn.getLocalName(0, 3) returns &quot;cn=john,o=example,c=us&quot;
   * dn.getLocalName(1, 2) returns &quot;o=example&quot;
   * dn.getLocalName(2, 2) returns &quot;&quot; (null DN).
   * </pre>
   *
   * </blockquote>
   *
   * @param beginIndex
   *          The index of the first RDN component (nearest the root),
   *          inclusive.
   * @param endIndex
   *          The index of the last RDN component (furthest from the
   *          root), exclusive.
   * @return Returns the specified local name.
   * @throws IndexOutOfBoundsException
   *           If <code>beginIndex</code> is negative, or
   *           <code>endIndex</code> is larger than the number of
   *           RDN components in this DN, or <code>beginIndex</code>
   *           is larger than <code>endIndex</code>.
   */
  public DN(DN baseDN, RDN[] rdnSequence)
  {
    assert debugConstructor(CLASS_NAME, String.valueOf(baseDN),
                            String.valueOf(rdnSequence));
  public DN getLocalName(int beginIndex, int endIndex)
      throws IndexOutOfBoundsException {
    if (beginIndex < 0) {
      throw new IndexOutOfBoundsException("beginIndex out of range: "
          + beginIndex);
    }
    RDN[] allRDNs = new RDN[rdnSequence.length+baseDN.numComponents];
    if (endIndex > numComponents) {
      throw new IndexOutOfBoundsException("endIndex out of range: "
          + endIndex);
    }
    System.arraycopy(rdnSequence, 0, allRDNs, 0, rdnSequence.length);
    System.arraycopy(baseDN.rdnComponents, 0, allRDNs,
                     rdnSequence.length, baseDN.numComponents);
    if (beginIndex > endIndex) {
      throw new IndexOutOfBoundsException(
          "beginIndex greater than endIndex");
    }
    this.rdnComponents = allRDNs;
    this.numComponents = allRDNs.length;
    this.dnString = null;
    this.normalizedDN = toNormalizedString();
    if (beginIndex == 0 && endIndex == numComponents) {
      return this;
    } else {
      int i = offset + numComponents - endIndex;
      return new DN(rdnComponents, i, endIndex - beginIndex);
    }
  }
  /**
   * Retrieves the set of RDN components that make up this DN,
   * arranged with the suffix as the last element. The caller must not
   * modify the contents of the returned list.
   * Get the number of RDN components that make up this DN.
   *
   * @return The set of RDN components that make up this DN.
   * @return Returns the number of RDN components that make up this
   *         DN.
   */
  public RDN[] getRDNComponents()
  {
    assert debugEnter(CLASS_NAME, "getRDNComponents");
  public int getNumComponents() {
    assert debugEnter(CLASS_NAME, "getNumComponents");
    return rdnComponents;
  }
  /**
   * Specifies the set of RDN components that make up this DN.
   *
   * @param  rdnComponents  The set of RDN components that make up
   *                        this DN arranged with the suffix as the
   *                        last element.
   */
  public void setRDNComponents(RDN[] rdnComponents)
  {
    assert debugEnter(CLASS_NAME, "setRDNComponents",
                      String.valueOf(rdnComponents));
    if (rdnComponents == null)
    {
      this.rdnComponents = new RDN[0];
    }
    else
    {
      this.rdnComponents = rdnComponents;
    }
    numComponents = this.rdnComponents.length;
    dnString      = null;
    normalizedDN  = null; // Get rid of the old cached value.
    normalizedDN  = toNormalizedString();
  }
  /**
   * Specifies the set of RDN components that make up this DN.
   *
   * @param  rdnComponents  The set of RDN components that make up
   *                        this DN, arranged with the suffix as the
   *                        last element.
   */
  public void setRDNComponents(ArrayList<RDN> rdnComponents)
  {
    assert debugEnter(CLASS_NAME, "setRDNComponents",
                      String.valueOf(rdnComponents));
    if (rdnComponents == null)
    {
      this.rdnComponents = new RDN[0];
    }
    else
    {
      this.rdnComponents = new RDN[rdnComponents.size()];
      rdnComponents.toArray(this.rdnComponents);
    }
    numComponents = this.rdnComponents.length;
    dnString      = null;
    normalizedDN  = toNormalizedString();
    return numComponents;
  }
  /**
   * Retrieves the outermost RDN component for this DN (i.e., the one
   * that is furthest from the suffix).
   * that is furthest from the suffix). This method is equivalent to
   * calling <code>getRDN(0)</code> for non-null DNs.
   *
   * @return  The outermost RDN component for this DN, or
   *          <CODE>null</CODE> if there are no RDN components in the
   *         <code>null</code> if there are no RDN components in the
   *          DN.
   */
  public RDN getRDN()
  {
  public RDN getRDN() {
    assert debugEnter(CLASS_NAME, "getRDN");
    if (numComponents == 0)
    {
    if (numComponents == 0) {
      return null;
    } else {
      return getRDN(0);
    }
    else
    {
      return rdnComponents[0];
    }
  /**
   * Get the RDN at the specified index.
   *
   * @param index
   *          The index of the RDN to retrieve, where <code>0</code>
   *          indicates the outermost RDN component (i.e. the one that
   *          is furthest from the suffix).
   * @return Returns the RDN at the specified index.
   * @throws IndexOutOfBoundsException
   *           If <code>index</code> is negative, or greater than or
   *           equal to the number of RDN components in this DN.
   */
  public RDN getRDN(int index) throws IndexOutOfBoundsException {
    if (index < 0) {
      throw new IndexOutOfBoundsException("index out of range: "
          + index);
    }
    if (index >= numComponents) {
      throw new IndexOutOfBoundsException("index out of range: "
          + index);
    }
    return rdnComponents[offset + index];
  }
@@ -304,25 +498,20 @@
   * Retrieves the DN of the entry that is the immediate parent for
   * this entry.
   *
   * @return  The DN of the entry that is the immediate parent for
   *          this entry, or <CODE>null</CODE> if the entry with this
   *          DN does not have a parent (either because there is only
   *          a single RDN component or because this DN is a suffix
   * @return The DN of the entry that is the immediate parent for this
   *         entry, or <code>null</code> if the entry with this DN
   *         does not have a parent (either because there is only a
   *         single RDN component or because this DN is a suffix
   *          defined in the server).
   */
  public DN getParent()
  {
    assert debugEnter(CLASS_NAME, "getParent");
  public DN getParentDNInSuffix() {
    assert debugEnter(CLASS_NAME, "getParentDNInSuffix");
    if ((numComponents <= 1) || (DirectoryServer.isSuffix(this)))
    {
    if ((numComponents <= 1) || (DirectoryServer.isSuffix(this))) {
      return null;
    }
    RDN[] parentComponents = new RDN[numComponents-1];
    System.arraycopy(rdnComponents, 1, parentComponents, 0,
                     numComponents-1);
    return new DN(parentComponents);
    return getParent();
  }
@@ -332,11 +521,10 @@
   * the root DSE for the Directory Server, or the authorization DN
   * for an anonymous or unauthenticated client.
   *
   * @return  <CODE>true</CODE> if this does represent a null DN, or
   *          <CODE>false</CODE> if it does not.
   * @return <code>true</code> if this does represent a null DN, or
   *         <code>false</code> if it does not.
   */
  public boolean isNullDN()
  {
  public boolean isNullDN() {
    assert debugEnter(CLASS_NAME, "isNullDN");
    return (numComponents == 0);
@@ -345,47 +533,28 @@
  /**
   * Indicates whether this DN is one of the suffixes defined in the
   * Directory Server.
   *
   * @return  <CODE>true</CODE> if this DN is one of the suffixes
   *          defined in the Directory Server, or <CODE>false</CODE>
   *          if not.
   */
  public boolean isSuffix()
  {
    assert debugEnter(CLASS_NAME, "isSuffix");
    return DirectoryServer.isSuffix(this);
  }
  /**
   * Indicates whether this DN is a descendant of the provided DN
   * (i.e., that the RDN components of the provided DN are the same as
   * the last RDN components for this DN).
   *
   * @param  dn  The DN for which to make the determination.
   *
   * @return  <CODE>true</CODE> if this DN is a descendant of the
   *          provided DN, or <CODE>false</CODE> if not.
   * @param dn
   *          The DN for which to make the determination.
   * @return <code>true</code> if this DN is a descendant of the
   *         provided DN, or <code>false</code> if not.
   */
  public boolean isDescendantOf(DN dn)
  {
    assert debugEnter(CLASS_NAME, "isDescendantOf",
                      String.valueOf(dn));
  public boolean isDescendantOf(DN dn) {
    assert debugEnter(CLASS_NAME, "isDescendantOf", String
        .valueOf(dn));
    int offset = numComponents - dn.numComponents;
    if (offset < 0)
    {
    ensureNotNull(dn);
    int diff = numComponents - dn.numComponents;
    if (diff < 0) {
      return false;
    }
    for (int i=0; i < dn.numComponents; i++)
    {
      if (! rdnComponents[i+offset].equals(dn.rdnComponents[i]))
      {
    for (int i = 0; i < dn.numComponents; i++) {
      if (!getRDN(i + diff).equals(dn.getRDN(i))) {
        return false;
      }
    }
@@ -400,25 +569,23 @@
   * (i.e., that the RDN components of this DN are the same as the
   * last RDN components for the provided DN).
   *
   * @param  dn  The DN for which to make the determination.
   *
   * @return  <CODE>true</CODE> if this DN is an ancestor of the
   *          provided DN, or <CODE>false</CODE> if not.
   * @param dn
   *          The DN for which to make the determination.
   * @return <code>true</code> if this DN is an ancestor of the
   *         provided DN, or <code>false</code> if not.
   */
  public boolean isAncestorOf(DN dn)
  {
  public boolean isAncestorOf(DN dn) {
    assert debugEnter(CLASS_NAME, "isAncestorOf", String.valueOf(dn));
    int offset = dn.numComponents - numComponents;
    if (offset < 0)
    {
    ensureNotNull(dn);
    int diff = dn.numComponents - numComponents;
    if (diff < 0) {
      return false;
    }
    for (int i=0; i < numComponents; i++)
    {
      if (! rdnComponents[i].equals(dn.rdnComponents[i+offset]))
      {
    for (int i = 0; i < numComponents; i++) {
      if (!getRDN(i).equals(dn.getRDN(i + diff))) {
        return false;
      }
    }
@@ -429,2334 +596,26 @@
  /**
   * Decodes the provided ASN.1 octet string as a DN.
   *
   * @param  dnString  The ASN.1 octet string to decode as a DN.
   *
   * @return  The decoded DN.
   *
   * @throws  DirectoryException  If a problem occurs while trying to
   *                              decode the provided ASN.1 octet
   *                              string as a DN.
   */
  public static DN decode(ByteString dnString)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "decode", String.valueOf(dnString));
    // A null or empty DN is acceptable.
    if (dnString == null)
    {
      return new DN(new ArrayList<RDN>(0));
    }
    byte[] dnBytes = dnString.value();
    int    length  = dnBytes.length;
    if (length == 0)
    {
      return new DN(new ArrayList<RDN>(0));
    }
    // See if we are dealing with any non-ASCII characters, or any
    // escaped characters.  If so, then the easiest and safest
    // approach is to convert the DN to a string and decode it that
    // way.
    for (byte b : dnBytes)
    {
      if (((b & 0x7F) != b) || (b == '\\'))
      {
        return decode(dnString.stringValue());
      }
    }
    // Iterate through the DN string.  The first thing to do is to get
    // rid of any leading spaces.
    int pos = 0;
    byte b = dnBytes[pos];
    while (b == ' ')
    {
      pos++;
      if (pos == length)
      {
        // This means that the DN was completely comprised of spaces
        // and therefore should be considered the same as a null or
        // empty DN.
        return new DN(new ArrayList<RDN>(0));
      }
      else
      {
        b = dnBytes[pos];
      }
    }
    // We know that it's not an empty DN, so we can do the real
    // processing.  Create a loop and iterate through all the RDN
    // components.
    boolean allowExceptions =
         DirectoryServer.allowAttributeNameExceptions();
    ArrayList<RDN> rdnComponents = new ArrayList<RDN>();
    while (true)
    {
      StringBuilder attributeName = new StringBuilder();
      pos = parseAttributeName(dnBytes, pos, attributeName,
                               allowExceptions);
      // Make sure that we're not at the end of the DN string because
      // that would be invalid.
      if (pos >= length)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
        String message = getMessage(msgID, dnString.stringValue(),
                                    attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces between the attribute name and its
      // value.
      b = dnBytes[pos];
      while (b == ' ')
      {
        pos++;
        if (pos >= length)
        {
          // This means that we hit the end of the value before
          // finding a '='.  This is illegal because there is no
          // attribute-value separator.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, dnString.stringValue(),
                                      attributeName.toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        else
        {
          b = dnBytes[pos];
        }
      }
      // The next character must be an equal sign.  If it is not,
      // then that's an error.
      if (b == '=')
      {
        pos++;
      }
      else
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_NO_EQUAL;
        String message = getMessage(msgID, dnString.stringValue(),
                                    attributeName.toString(),
                                    (char) b);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces after the equal sign.
      while ((pos < length) && ((b = dnBytes[pos]) == ' '))
      {
        pos++;
      }
      // If we are at the end of the DN string, then that must mean
      // that the attribute value was empty.  This will probably never
      // happen in a real-world environment, but technically isn't
      // illegal.  If it does happen, then go ahead and create the RDN
      // component and return the DN.
      if (pos >= length)
      {
        String        name      = attributeName.toString();
        String        lowerName = toLowerCase(name);
        AttributeType attrType  =
             DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          // This must be an attribute type that we don't know about.
          // In that case, we'll create a new attribute using the
          // default syntax.  If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        AttributeValue value =
             new AttributeValue(new ASN1OctetString(),
                                new ASN1OctetString());
        rdnComponents.add(new RDN(attrType, name, value));
        return new DN(rdnComponents);
      }
      // Parse the value for this RDN component.
      ByteString parsedValue = new ASN1OctetString();
      pos = parseAttributeValue(dnBytes, pos, parsedValue);
      // Create the new RDN with the provided information.
      String name            = attributeName.toString();
      String lowerName       = toLowerCase(name);
      AttributeType attrType =
           DirectoryServer.getAttributeType(lowerName);
      if (attrType == null)
      {
        // This must be an attribute type that we don't know about.
        // In that case, we'll create a new attribute using the
        // default syntax.  If this is a problem, it will be caught
        // later either by not finding the target entry or by not
        // allowing the entry to be added.
        attrType = DirectoryServer.getDefaultAttributeType(name);
      }
      AttributeValue value =
           new AttributeValue(attrType, parsedValue);
      RDN rdn = new RDN(attrType, name, value);
      // Skip over any spaces that might be after the attribute value.
      while ((pos < length) && ((b = dnBytes[pos]) == ' '))
      {
        pos++;
      }
      // Most likely, we will be at either the end of the RDN
      // component or the end of the DN.  If so, then handle that
      // appropriately.
      if (pos >= length)
      {
        // We're at the end of the DN string and should have a valid
        // DN so return it.
        rdnComponents.add(rdn);
        return new DN(rdnComponents);
      }
      else if ((b == ',') || (b == ';'))
      {
        // We're at the end of the RDN component, so add it to the
        // list, skip over the comma/semicolon, and start on the next
        // component.
        rdnComponents.add(rdn);
        pos++;
        continue;
      }
      else if (b != '+')
      {
        // This should not happen.  At any rate, it's an illegal
        // character, so throw an exception.
        int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_CHAR;
        String message = getMessage(msgID, new String(dnBytes),
                                    (char) b, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // If we have gotten here, then this must be a multi-valued RDN.
      // In that case, parse the remaining attribute/value pairs and
      // add them to the RDN that we've already created.
      while (true)
      {
        // Skip over the plus sign and any spaces that may follow it
        // before the next attribute name.
        pos++;
        while ((pos < length) && (dnBytes[pos] == ' '))
        {
          pos++;
        }
        // Parse the attribute name from the DN string.
        attributeName = new StringBuilder();
        pos = parseAttributeName(dnBytes, pos, attributeName,
                                 allowExceptions);
        // Make sure that we're not at the end of the DN string
        // because that would be invalid.
        if (pos >= length)
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, dnString.stringValue(),
                                      attributeName.toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        // Skip over any spaces between the attribute name and its
        // value.
        b = dnBytes[pos];
        while (b == ' ')
        {
          pos++;
          if (pos >= length)
          {
            // This means that we hit the end of the value before
            // finding a '='.  This is illegal because there is no
            // attribute-value separator.
            int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
            String message = getMessage(msgID, dnString.stringValue(),
                                        attributeName.toString());
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          else
          {
            b = dnBytes[pos];
          }
        }
        // The next character must be an equal sign.  If it is not,
        // then that's an error.
        if (b == '=')
        {
          pos++;
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_NO_EQUAL;
          String message = getMessage(msgID, dnString.stringValue(),
                                      attributeName.toString(),
                                      (char) b);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        // Skip over any spaces after the equal sign.
        while ((pos < length) && ((b = dnBytes[pos]) == ' '))
        {
          pos++;
        }
        // If we are at the end of the DN string, then that must mean
        // that the attribute value was empty.  This will probably
        // never happen in a real-world environment, but technically
        // isn't illegal.  If it does happen, then go ahead and create
        // the RDN component and return the DN.
        if (pos >= length)
        {
          name      = attributeName.toString();
          lowerName = toLowerCase(name);
          attrType  = DirectoryServer.getAttributeType(lowerName);
          if (attrType == null)
          {
            // This must be an attribute type that we don't know
            // about.  In that case, we'll create a new attribute
            // using the default syntax.  If this is a problem, it
            // will be caught later either by not finding the target
            // entry or by not allowing the entry to be added.
            attrType = DirectoryServer.getDefaultAttributeType(name);
          }
          value = new AttributeValue(new ASN1OctetString(),
                                     new ASN1OctetString());
          rdn.addValue(attrType, name, value);
          rdnComponents.add(rdn);
          return new DN(rdnComponents);
        }
        // Parse the value for this RDN component.
        parsedValue = new ASN1OctetString();
        pos = parseAttributeValue(dnBytes, pos, parsedValue);
        // Create the new RDN with the provided information.
        name      = attributeName.toString();
        lowerName = toLowerCase(name);
        attrType  = DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          // This must be an attribute type that we don't know about.
          // In that case, we'll create a new attribute using the
          // default syntax.  If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        value = new AttributeValue(attrType, parsedValue);
        rdn.addValue(attrType, name, value);
        // Skip over any spaces that might be after the attribute
        // value.
        while ((pos < length) && ((b = dnBytes[pos]) == ' '))
        {
          pos++;
        }
        // Most likely, we will be at either the end of the RDN
        // component or the end of the DN.  If so, then handle that
        // appropriately.
        if (pos >= length)
        {
          // We're at the end of the DN string and should have a valid
          // DN so return it.
          rdnComponents.add(rdn);
          return new DN(rdnComponents);
        }
        else if ((b == ',') || (b == ';'))
        {
          // We're at the end of the RDN component, so add it to the
          // list, skip over the comma/semicolon, and start on the
          // next component.
          rdnComponents.add(rdn);
          pos++;
          break;
        }
        else if (b != '+')
        {
          // This should not happen.  At any rate, it's an illegal
          // character, so throw an exception.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_CHAR;
          String message = getMessage(msgID, dnString.stringValue(),
                                      (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
    }
  }
  /**
   * Decodes the provided string as a DN.
   *
   * @param  dnString  The string to decode as a DN.
   *
   * @return  The decoded DN.
   *
   * @throws  DirectoryException  If a problem occurs while trying to
   *                              decode the provided string as a DN.
   */
  public static DN decode(String dnString)
         throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "decode", String.valueOf(dnString));
    // A null or empty DN is acceptable.
    if (dnString == null)
    {
      return new DN(new ArrayList<RDN>(0));
    }
    int length = dnString.length();
    if (length == 0)
    {
      return new DN(new ArrayList<RDN>(0));
    }
    // Iterate through the DN string.  The first thing to do is to get
    // rid of any leading spaces.
    int pos = 0;
    char c = dnString.charAt(pos);
    while (c == ' ')
    {
      pos++;
      if (pos == length)
      {
        // This means that the DN was completely comprised of spaces
        // and therefore should be considered the same as a null or
        // empty DN.
        return new DN(new ArrayList<RDN>(0));
      }
      else
      {
        c = dnString.charAt(pos);
      }
    }
    // We know that it's not an empty DN, so we can do the real
    // processing.  Create a loop and iterate through all the RDN
    // components.
    boolean allowExceptions =
         DirectoryServer.allowAttributeNameExceptions();
    ArrayList<RDN> rdnComponents = new ArrayList<RDN>();
    while (true)
    {
      StringBuilder attributeName = new StringBuilder();
      pos = parseAttributeName(dnString, pos, attributeName,
                               allowExceptions);
      // Make sure that we're not at the end of the DN string because
      // that would be invalid.
      if (pos >= length)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
        String message = getMessage(msgID, dnString,
                                    attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces between the attribute name and its
      // value.
      c = dnString.charAt(pos);
      while (c == ' ')
      {
        pos++;
        if (pos >= length)
        {
          // This means that we hit the end of the value before
          // finding a '='.  This is illegal because there is no
          // attribute-value separator.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, dnString,
                                      attributeName.toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        else
        {
          c = dnString.charAt(pos);
        }
      }
      // The next character must be an equal sign.  If it is not, then
      // that's an error.
      if (c == '=')
      {
        pos++;
      }
      else
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_NO_EQUAL;
        String message = getMessage(msgID, dnString,
                                    attributeName.toString(), c);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces after the equal sign.
      while ((pos < length) && ((c = dnString.charAt(pos)) == ' '))
      {
        pos++;
      }
      // If we are at the end of the DN string, then that must mean
      // that the attribute value was empty.  This will probably never
      // happen in a real-world environment, but technically isn't
      // illegal.  If it does happen, then go ahead and create the
      // RDN component and return the DN.
      if (pos >= length)
      {
        String        name      = attributeName.toString();
        String        lowerName = toLowerCase(name);
        AttributeType attrType  =
             DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          // This must be an attribute type that we don't know about.
          // In that case, we'll create a new attribute using the
          // default syntax.  If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        AttributeValue value =
             new AttributeValue(new ASN1OctetString(),
                                new ASN1OctetString());
        rdnComponents.add(new RDN(attrType, name, value));
        return new DN(rdnComponents);
      }
      // Parse the value for this RDN component.
      ByteString parsedValue = new ASN1OctetString();
      pos = parseAttributeValue(dnString, pos, parsedValue);
      // Create the new RDN with the provided information.
      String name            = attributeName.toString();
      String lowerName       = toLowerCase(name);
      AttributeType attrType =
           DirectoryServer.getAttributeType(lowerName);
      if (attrType == null)
      {
        // This must be an attribute type that we don't know about.
        // In that case, we'll create a new attribute using the
        // default syntax.  If this is a problem, it will be caught
        // later either by not finding the target entry or by not
        // allowing the entry to be added.
        attrType = DirectoryServer.getDefaultAttributeType(name);
      }
      AttributeValue value =
           new AttributeValue(attrType, parsedValue);
      RDN rdn = new RDN(attrType, name, value);
      // Skip over any spaces that might be after the attribute value.
      while ((pos < length) && ((c = dnString.charAt(pos)) == ' '))
      {
        pos++;
      }
      // Most likely, we will be at either the end of the RDN
      // component or the end of the DN.  If so, then handle that
      // appropriately.
      if (pos >= length)
      {
        // We're at the end of the DN string and should have a valid
        // DN so return it.
        rdnComponents.add(rdn);
        return new DN(rdnComponents);
      }
      else if ((c == ',') || (c == ';'))
      {
        // We're at the end of the RDN component, so add it to the
        // list, skip over the comma/semicolon, and start on the next
        // component.
        rdnComponents.add(rdn);
        pos++;
        continue;
      }
      else if (c != '+')
      {
        // This should not happen.  At any rate, it's an illegal
        // character, so throw an exception.
        int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_CHAR;
        String message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // If we have gotten here, then this must be a multi-valued RDN.
      // In that case, parse the remaining attribute/value pairs and
      // add them to the RDN that we've already created.
      while (true)
      {
        // Skip over the plus sign and any spaces that may follow it
        // before the next attribute name.
        pos++;
        while ((pos < length) && (dnString.charAt(pos) == ' '))
        {
          pos++;
        }
        // Parse the attribute name from the DN string.
        attributeName = new StringBuilder();
        pos = parseAttributeName(dnString, pos, attributeName,
                                 allowExceptions);
        // Make sure that we're not at the end of the DN string
        // because that would be invalid.
        if (pos >= length)
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, dnString,
                                      attributeName.toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        // Skip over any spaces between the attribute name and its
        // value.
        c = dnString.charAt(pos);
        while (c == ' ')
        {
          pos++;
          if (pos >= length)
          {
            // This means that we hit the end of the value before
            // finding a '='.  This is illegal because there is no
            // attribute-value separator.
            int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
            String message = getMessage(msgID, dnString,
                                        attributeName.toString());
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          else
          {
            c = dnString.charAt(pos);
          }
        }
        // The next character must be an equal sign.  If it is not,
        // then that's an error.
        if (c == '=')
        {
          pos++;
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_NO_EQUAL;
          String message = getMessage(msgID, dnString,
                                      attributeName.toString(), c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        // Skip over any spaces after the equal sign.
        while ((pos < length) && ((c = dnString.charAt(pos)) == ' '))
        {
          pos++;
        }
        // If we are at the end of the DN string, then that must mean
        // that the attribute value was empty.  This will probably
        // never happen in a real-world environment, but technically
        // isn't illegal.  If it does happen, then go ahead and create
        // the RDN component and return the DN.
        if (pos >= length)
        {
          name      = attributeName.toString();
          lowerName = toLowerCase(name);
          attrType  = DirectoryServer.getAttributeType(lowerName);
          if (attrType == null)
          {
            // This must be an attribute type that we don't know
            // about.  In that case, we'll create a new attribute
            // using the default syntax.  If this is a problem, it
            // will be caught later either by not finding the target
            // entry or by not allowing the entry to be added.
            attrType = DirectoryServer.getDefaultAttributeType(name);
          }
          value = new AttributeValue(new ASN1OctetString(),
                                     new ASN1OctetString());
          rdn.addValue(attrType, name, value);
          rdnComponents.add(rdn);
          return new DN(rdnComponents);
        }
        // Parse the value for this RDN component.
        parsedValue = new ASN1OctetString();
        pos = parseAttributeValue(dnString, pos, parsedValue);
        // Create the new RDN with the provided information.
        name      = attributeName.toString();
        lowerName = toLowerCase(name);
        attrType  = DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          // This must be an attribute type that we don't know about.
          // In that case, we'll create a new attribute using the
          // default syntax.  If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        value = new AttributeValue(attrType, parsedValue);
        rdn.addValue(attrType, name, value);
        // Skip over any spaces that might be after the attribute
        // value.
        while ((pos < length) && ((c = dnString.charAt(pos)) == ' '))
        {
          pos++;
        }
        // Most likely, we will be at either the end of the RDN
        // component or the end of the DN.  If so, then handle that
        // appropriately.
        if (pos >= length)
        {
          // We're at the end of the DN string and should have a valid
          // DN so return it.
          rdnComponents.add(rdn);
          return new DN(rdnComponents);
        }
        else if ((c == ',') || (c == ';'))
        {
          // We're at the end of the RDN component, so add it to the
          // list, skip over the comma/semicolon, and start on the
          // next component.
          rdnComponents.add(rdn);
          pos++;
          break;
        }
        else if (c != '+')
        {
          // This should not happen.  At any rate, it's an illegal
          // character, so throw an exception.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_CHAR;
          String message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
    }
  }
  /**
   * Parses an attribute name from the provided DN string starting at
   * the specified location.
   *
   * @param  dnBytes          The byte array containing the DN to
   *                          parse.
   * @param  pos              The position at which to start parsing
   *                          the attribute name.
   * @param  attributeName    The buffer to which to append the parsed
   *                          attribute name.
   * @param  allowExceptions  Indicates whether to allow certain
   *                          exceptions to the strict requirements
   *                          for attribute names.
   *
   * @return  The position of the first character that is not part of
   *          the attribute name.
   *
   * @throws  DirectoryException  If it was not possible to parse a
   *                              valid attribute name from the
   *                              provided DN string.
   */
  public static int parseAttributeName(byte[] dnBytes, int pos,
                                       StringBuilder attributeName,
                                       boolean allowExceptions)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "parseAttributeName",
                      String.valueOf(dnBytes), String.valueOf(pos),
                      "java.lang.StringBuilder");
    int length = dnBytes.length;
    // Skip over any leading spaces.
    if (pos < length)
    {
      while (dnBytes[pos] == ' ')
      {
        pos++;
        if (pos == length)
        {
          // This means that the remainder of the DN was completely
          // comprised of spaces.  If we have gotten here, then we
          // know that there is at least one RDN component, and
          // therefore the last non-space character of the DN must
          // have been a comma. This is not acceptable.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_COMMA;
          String message = getMessage(msgID, new String(dnBytes));
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
    }
    // Next, we should find the attribute name for this RDN component.
    // It may either be a name (with only letters, digits, and dashes
    // and starting with a letter) or an OID (with only digits and
    // periods, optionally prefixed with "oid."), and there is also a
    // special case in which we will allow underscores.  Because of
    // the complexity involved, read the entire name first with
    // minimal validation and then do more thorough validation later.
    boolean       checkForOID   = false;
    boolean       endOfName     = false;
    while (pos < length)
    {
      // To make the switch more efficient, we'll include all ASCII
      // characters in the range of allowed values and then reject the
      // ones that aren't allowed.
      byte b = dnBytes[pos];
      switch (b)
      {
        case ' ':
          // This should denote the end of the attribute name.
          endOfName = true;
          break;
        case '!':
        case '"':
        case '#':
        case '$':
        case '%':
        case '&':
        case '\'':
        case '(':
        case ')':
        case '*':
        case '+':
        case ',':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          String message = getMessage(msgID, new String(dnBytes),
                                      (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '-':
          // This will be allowed as long as it isn't the first
          // character in the attribute name.
          if (attributeName.length() > 0)
          {
            attributeName.append((char) b);
          }
          else
          {
            msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DASH;
            message = getMessage(msgID, new String(dnBytes),
                                 (char) b);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          break;
        case '.':
          // The period could be allowed if the attribute name is
          // actually expressed as an OID.  We'll accept it for now,
          // but make sure to check it later.
          attributeName.append((char) b);
          checkForOID = true;
          break;
        case '/':
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          // Digits are always allowed if they are not the first
          // character.  However, they may be allowed if they are the
          // first character if the valid is an OID or if the
          // attribute name exceptions option is enabled.  Therefore,
          // we'll accept it now and check it later.
          attributeName.append((char) b);
          break;
        case ':':
        case ';': // NOTE:  attribute options are not allowed in a DN.
        case '<':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '=':
          // This should denote the end of the attribute name.
          endOfName = true;
          break;
        case '>':
        case '?':
        case '@':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
        case 'G':
        case 'H':
        case 'I':
        case 'J':
        case 'K':
        case 'L':
        case 'M':
        case 'N':
        case 'O':
        case 'P':
        case 'Q':
        case 'R':
        case 'S':
        case 'T':
        case 'U':
        case 'V':
        case 'W':
        case 'X':
        case 'Y':
        case 'Z':
          // These will always be allowed.
          attributeName.append((char) b);
          break;
        case '[':
        case '\\':
        case ']':
        case '^':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '_':
          // This will never be allowed as the first character.  It
          // may be allowed for subsequent characters if the attribute
          // name exceptions option is enabled.
          if (attributeName.length() == 0)
          {
            msgID   =
                 MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_UNDERSCORE;
            message = getMessage(msgID, new String(dnBytes),
                           ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          else if (allowExceptions)
          {
            attributeName.append((char) b);
          }
          else
          {
            msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_UNDERSCORE_CHAR;
            message = getMessage(msgID, new String(dnBytes),
                           ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          break;
        case '`':
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'g':
        case 'h':
        case 'i':
        case 'j':
        case 'k':
        case 'l':
        case 'm':
        case 'n':
        case 'o':
        case 'p':
        case 'q':
        case 'r':
        case 's':
        case 't':
        case 'u':
        case 'v':
        case 'w':
        case 'x':
        case 'y':
        case 'z':
          // These will always be allowed.
          attributeName.append((char) b);
          break;
        default:
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, new String(dnBytes),
                               (char) b, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
      }
      if (endOfName)
      {
        break;
      }
      pos++;
    }
    // We should now have the full attribute name.  However, we may
    // still need to perform some validation, particularly if the name
    // contains a period or starts with a digit.  It must also have at
    // least one character.
    if (attributeName.length() == 0)
    {
      int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_NO_NAME;
      String message = getMessage(msgID, new String(dnBytes));
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    else if (checkForOID)
    {
      boolean validOID = true;
      int namePos = 0;
      int nameLength = attributeName.length();
      char ch = attributeName.charAt(0);
      if ((ch == 'o') || (ch == 'O'))
      {
        if (nameLength <= 4)
        {
          validOID = false;
        }
        else
        {
          if ((((ch = attributeName.charAt(1)) == 'i') ||
               (ch == 'I')) &&
              (((ch = attributeName.charAt(2)) == 'd') ||
               (ch == 'D')) &&
              (attributeName.charAt(3) == '.'))
          {
            attributeName.delete(0, 4);
            nameLength -= 4;
          }
          else
          {
            validOID = false;
          }
        }
      }
      while (validOID && (namePos < nameLength))
      {
        ch = attributeName.charAt(namePos++);
        if (isDigit(ch))
        {
          while (validOID && (namePos < nameLength) &&
                 isDigit(attributeName.charAt(namePos)))
          {
            namePos++;
          }
          if ((namePos < nameLength) &&
              (attributeName.charAt(namePos) != '.'))
          {
            validOID = false;
          }
        }
        else if (ch == '.')
        {
          if ((namePos == 1) ||
              (attributeName.charAt(namePos-2) == '.'))
          {
            validOID = false;
          }
        }
        else
        {
          validOID = false;
        }
      }
      if (validOID && (attributeName.charAt(nameLength-1) == '.'))
      {
        validOID = false;
      }
      if (! validOID)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_PERIOD;
        String message = getMessage(msgID, new String(dnBytes),
                                    attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
    }
    else if (isDigit(attributeName.charAt(0)) && (! allowExceptions))
    {
      int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DIGIT;
      String message = getMessage(msgID, new String(dnBytes),
                            attributeName.charAt(0),
                            ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    return pos;
  }
  /**
   * Parses an attribute name from the provided DN string starting at
   * the specified location.
   *
   * @param  dnString         The DN string to be parsed.
   * @param  pos              The position at which to start parsing
   *                          the attribute name.
   * @param  attributeName    The buffer to which to append the parsed
   *                          attribute name.
   * @param  allowExceptions  Indicates whether to allow certain
   *                          exceptions to the strict requirements
   *                          for attribute names.
   *
   * @return  The position of the first character that is not part of
   *          the attribute name.
   *
   * @throws  DirectoryException  If it was not possible to parse a
   *                              valid attribute name from the
   *                              provided DN string.
   */
  public static int parseAttributeName(String dnString, int pos,
                                       StringBuilder attributeName,
                                       boolean allowExceptions)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "parseAttributeName",
                      String.valueOf(dnString), String.valueOf(pos),
                      "java.lang.StringBuilder");
    int length = dnString.length();
    // Skip over any leading spaces.
    if (pos < length)
    {
      while (dnString.charAt(pos) == ' ')
      {
        pos++;
        if (pos == length)
        {
          // This means that the remainder of the DN was completely
          // comprised of spaces.  If we have gotten here, then we
          // know that there is at least one RDN component, and
          // therefore the last non-space character of the DN must
          // have been a comma. This is not acceptable.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_END_WITH_COMMA;
          String message = getMessage(msgID, dnString);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
    }
    // Next, we should find the attribute name for this RDN component.
    // It may either be a name (with only letters, digits, and dashes
    // and starting with a letter) or an OID (with only digits and
    // periods, optionally prefixed with "oid."), and there is also a
    // special case in which we will allow underscores.  Because of
    // the complexity involved, read the entire name first with
    // minimal validation and then do more thorough validation later.
    boolean       checkForOID   = false;
    boolean       endOfName     = false;
    while (pos < length)
    {
      // To make the switch more efficient, we'll include all ASCII
      // characters in the range of allowed values and then reject the
      // ones that aren't allowed.
      char c = dnString.charAt(pos);
      switch (c)
      {
        case ' ':
          // This should denote the end of the attribute name.
          endOfName = true;
          break;
        case '!':
        case '"':
        case '#':
        case '$':
        case '%':
        case '&':
        case '\'':
        case '(':
        case ')':
        case '*':
        case '+':
        case ',':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          String message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '-':
          // This will be allowed as long as it isn't the first
          // character in the attribute name.
          if (attributeName.length() > 0)
          {
            attributeName.append(c);
          }
          else
          {
            msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DASH;
            message = getMessage(msgID, dnString, c);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          break;
        case '.':
          // The period could be allowed if the attribute name is
          // actually expressed as an OID.  We'll accept it for now,
          // but make sure to check it later.
          attributeName.append(c);
          checkForOID = true;
          break;
        case '/':
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
        case '8':
        case '9':
          // Digits are always allowed if they are not the first
          // character. However, they may be allowed if they are the
          // first character if the valid is an OID or if the
          // attribute name exceptions option is enabled.  Therefore,
          // we'll accept it now and check it later.
          attributeName.append(c);
          break;
        case ':':
        case ';': // NOTE:  attribute options are not allowed in a DN.
        case '<':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '=':
          // This should denote the end of the attribute name.
          endOfName = true;
          break;
        case '>':
        case '?':
        case '@':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case 'A':
        case 'B':
        case 'C':
        case 'D':
        case 'E':
        case 'F':
        case 'G':
        case 'H':
        case 'I':
        case 'J':
        case 'K':
        case 'L':
        case 'M':
        case 'N':
        case 'O':
        case 'P':
        case 'Q':
        case 'R':
        case 'S':
        case 'T':
        case 'U':
        case 'V':
        case 'W':
        case 'X':
        case 'Y':
        case 'Z':
          // These will always be allowed.
          attributeName.append(c);
          break;
        case '[':
        case '\\':
        case ']':
        case '^':
          // None of these are allowed in an attribute name or any
          // character immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case '_':
          // This will never be allowed as the first character.  It
          // may be allowed for subsequent characters if the attribute
          // name exceptions option is enabled.
          if (attributeName.length() == 0)
          {
            msgID   =
                 MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_UNDERSCORE;
            message = getMessage(msgID, dnString,
                           ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          else if (allowExceptions)
          {
            attributeName.append(c);
          }
          else
          {
            msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_UNDERSCORE_CHAR;
            message = getMessage(msgID, dnString,
                           ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
          break;
        case '`':
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        case 'a':
        case 'b':
        case 'c':
        case 'd':
        case 'e':
        case 'f':
        case 'g':
        case 'h':
        case 'i':
        case 'j':
        case 'k':
        case 'l':
        case 'm':
        case 'n':
        case 'o':
        case 'p':
        case 'q':
        case 'r':
        case 's':
        case 't':
        case 'u':
        case 'v':
        case 'w':
        case 'x':
        case 'y':
        case 'z':
          // These will always be allowed.
          attributeName.append(c);
          break;
        default:
          // This is not allowed in an attribute name or any character
          // immediately following it.
          msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
          message = getMessage(msgID, dnString, c, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
      }
      if (endOfName)
      {
        break;
      }
      pos++;
    }
    // We should now have the full attribute name.  However, we may
    // still need to perform some validation, particularly if the
    // name contains a period or starts with a digit.  It must also
    // have at least one character.
    if (attributeName.length() == 0)
    {
      int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_NO_NAME;
      String message = getMessage(msgID, dnString);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    else if (checkForOID)
    {
      boolean validOID = true;
      int namePos = 0;
      int nameLength = attributeName.length();
      char ch = attributeName.charAt(0);
      if ((ch == 'o') || (ch == 'O'))
      {
        if (nameLength <= 4)
        {
          validOID = false;
        }
        else
        {
          if ((((ch = attributeName.charAt(1)) == 'i') ||
               (ch == 'I')) &&
              (((ch = attributeName.charAt(2)) == 'd') ||
               (ch == 'D')) &&
              (attributeName.charAt(3) == '.'))
          {
            attributeName.delete(0, 4);
            nameLength -= 4;
          }
          else
          {
            validOID = false;
          }
        }
      }
      while (validOID && (namePos < nameLength))
      {
        ch = attributeName.charAt(namePos++);
        if (isDigit(ch))
        {
          while (validOID && (namePos < nameLength) &&
                 isDigit(attributeName.charAt(namePos)))
          {
            namePos++;
          }
          if ((namePos < nameLength) &&
              (attributeName.charAt(namePos) != '.'))
          {
            validOID = false;
          }
        }
        else if (ch == '.')
        {
          if ((namePos == 1) ||
              (attributeName.charAt(namePos-2) == '.'))
          {
            validOID = false;
          }
        }
        else
        {
          validOID = false;
        }
      }
      if (validOID && (attributeName.charAt(nameLength-1) == '.'))
      {
        validOID = false;
      }
      if (! validOID)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_PERIOD;
        String message = getMessage(msgID, dnString,
                                    attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
    }
    else if (isDigit(attributeName.charAt(0)) &&
             (! allowExceptions))
    {
      int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DIGIT;
      String message = getMessage(msgID, dnString,
                            attributeName.charAt(0),
                            ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    return pos;
  }
  /**
   * Parses the attribute value from the provided DN string starting
   * at the specified location.  When the value has been parsed, it
   * will be assigned to the provided ASN.1 octet string.
   *
   * @param  dnBytes         The byte array containing the DN to be
   *                         parsed.
   * @param  pos             The position of the first character in
   *                         the attribute value to parse.
   * @param  attributeValue  The ASN.1 octet string whose value should
   *                         be set to the parsed attribute value when
   *                         this method completes successfully.
   *
   * @return  The position of the first character that is not part of
   *          the attribute value.
   *
   * @throws  DirectoryException  If it was not possible to parse a
   *                              valid attribute value from the
   *                              provided DN string.
   */
  public static int parseAttributeValue(byte[] dnBytes, int pos,
                                        ByteString attributeValue)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "parseAttributeValue",
                      String.valueOf(dnBytes), String.valueOf(pos),
                      "java.lang.StringBuilder");
    // All leading spaces have already been stripped so we can start
    // reading the value.  However, it may be empty so check for that.
    int length = dnBytes.length;
    if (pos >= length)
    {
      attributeValue.setValue("");
      return pos;
    }
    // Look at the first character.  If it is an octothorpe (#), then
    // that means that the value should be a hex string.
    byte b = dnBytes[pos++];
    if (b == '#')
    {
      // The first two characters must be hex characters.
      StringBuilder hexString = new StringBuilder();
      if ((pos+2) > length)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
        String message = getMessage(msgID, new String(dnBytes));
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      for (int i=0; i < 2; i++)
      {
        b = dnBytes[pos++];
        if (isHexDigit(b))
        {
          hexString.append((char) b);
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, new String(dnBytes),
                                      (char) b);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
      // The rest of the value must be a multiple of two hex
      // characters.  The end of the value may be designated by the
      // end of the DN, a comma or semicolon, a plus sign, or a space.
      while (pos < length)
      {
        b = dnBytes[pos++];
        if (isHexDigit(b))
        {
          hexString.append((char) b);
          if (pos < length)
          {
            b = dnBytes[pos++];
            if (isHexDigit(b))
            {
              hexString.append((char) b);
            }
            else
            {
              int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
              String message = getMessage(msgID, new String(dnBytes),
                                          (char) b);
              throw new DirectoryException(
                             ResultCode.INVALID_DN_SYNTAX, message,
                             msgID);
            }
          }
          else
          {
            int    msgID   = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
            String message = getMessage(msgID, new String(dnBytes));
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
        }
        else if ((b == ' ') || (b == ',') || (b == ';') || (b == '+'))
        {
          // This denotes the end of the value.
          pos--;
          break;
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, new String(dnBytes),
                                      (char) b);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
      // At this point, we should have a valid hex string.  Convert it
      // to a byte array and set that as the value of the provided
      // octet string.
      try
      {
        attributeValue.setValue(hexStringToByteArray(
                                     hexString.toString()));
        return pos;
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "parseAttributeValue", e);
        int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_VALUE_DECODE_FAILURE;
        String message = getMessage(msgID, new String(dnBytes),
                                    String.valueOf(e));
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
    }
    // If the first character is a quotation mark, then the value
    // should continue until the corresponding closing quotation mark.
    else if (b == '"')
    {
      int valueStartPos = pos;
      // Keep reading until we find a closing quotation mark.
      while (true)
      {
        if (pos >= length)
        {
          // We hit the end of the DN before the closing quote.
          // That's an error.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_UNMATCHED_QUOTE;
          String message = getMessage(msgID, new String(dnBytes));
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        if (dnBytes[pos++] == '"')
        {
          // This is the end of the value.
          break;
        }
      }
      byte[] valueBytes = new byte[pos - valueStartPos - 1];
      System.arraycopy(dnBytes, valueStartPos, valueBytes, 0,
                       valueBytes.length);
      try
      {
        attributeValue.setValue(valueBytes);
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "parseAttributeValue", e);
        // This should never happen.  Just in case, work around it by
        // converting to a string and back.
        String valueStr = new String(valueBytes);
        attributeValue.setValue(valueStr);
      }
      return pos;
    }
    // Otherwise, use general parsing to find the end of the value.
    else
    {
      // Keep reading until we find a comma/semicolon, a plus sign, or
      // the end of the DN.
      int valueStartPos = pos - 1;
      while (true)
      {
        if (pos >= length)
        {
          // This is the end of the DN and therefore the end of the
          // value.
          break;
        }
        b = dnBytes[pos++];
        if ((b == ',') || (b == ';') || (b == '+'))
        {
          pos--;
          break;
        }
      }
      // Convert the byte buffer to an array.
      byte[] valueBytes = new byte[pos - valueStartPos];
      System.arraycopy(dnBytes, valueStartPos, valueBytes, 0,
                       valueBytes.length);
      // Strip off any unescaped spaces that may be at the end of the
      // value.
      boolean extraSpaces = false;
      int     lastPos     = valueBytes.length - 1;
      while (lastPos > 0)
      {
        if (valueBytes[lastPos] == ' ')
        {
          extraSpaces = true;
          lastPos--;
        }
        else
        {
          break;
        }
      }
      if (extraSpaces)
      {
        byte[] newValueBytes = new byte[lastPos+1];
        System.arraycopy(valueBytes, 0, newValueBytes, 0, lastPos+1);
        valueBytes = newValueBytes;
      }
      try
      {
        attributeValue.setValue(valueBytes);
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "parseAttributeValue", e);
        // This should never happen.  Just in case, work around it by
        // converting to a string and back.
        String valueStr = new String(valueBytes);
        attributeValue.setValue(valueStr);
      }
      return pos;
    }
  }
  /**
   * Parses the attribute value from the provided DN string starting
   * at the specified location.  When the value has been parsed, it
   * will be assigned to the provided ASN.1 octet string.
   *
   * @param  dnString        The DN string to be parsed.
   * @param  pos             The position of the first character in
   *                         the attribute value to parse.
   * @param  attributeValue  The ASN.1 octet string whose value should
   *                         be set to the parsed attribute value when
   *                         this method completes successfully.
   *
   * @return  The position of the first character that is not part of
   *          the attribute value.
   *
   * @throws  DirectoryException  If it was not possible to parse a
   *                              valid attribute value from the
   *                              provided DN string.
   */
  public static int parseAttributeValue(String dnString, int pos,
                                        ByteString attributeValue)
          throws DirectoryException
  {
    assert debugEnter(CLASS_NAME, "parseAttributeValue",
                      String.valueOf(dnString), String.valueOf(pos),
                      "java.lang.StringBuilder");
    // All leading spaces have already been stripped so we can start
    // reading the value.  However, it may be empty so check for that.
    int length = dnString.length();
    if (pos >= length)
    {
      attributeValue.setValue("");
      return pos;
    }
    // Look at the first character.  If it is an octothorpe (#), then
    // that means that the value should be a hex string.
    char c = dnString.charAt(pos++);
    if (c == '#')
    {
      // The first two characters must be hex characters.
      StringBuilder hexString = new StringBuilder();
      if ((pos+2) > length)
      {
        int    msgID   = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
        String message = getMessage(msgID, dnString);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      for (int i=0; i < 2; i++)
      {
        c = dnString.charAt(pos++);
        if (isHexDigit(c))
        {
          hexString.append(c);
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, dnString, c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
      // The rest of the value must be a multiple of two hex
      // characters.  The end of the value may be designated by the
      // end of the DN, a comma or semicolon, or a space.
      while (pos < length)
      {
        c = dnString.charAt(pos++);
        if (isHexDigit(c))
        {
          hexString.append(c);
          if (pos < length)
          {
            c = dnString.charAt(pos++);
            if (isHexDigit(c))
            {
              hexString.append(c);
            }
            else
            {
              int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
              String message = getMessage(msgID, dnString, c);
              throw new DirectoryException(
                             ResultCode.INVALID_DN_SYNTAX, message,
                             msgID);
            }
          }
          else
          {
            int    msgID   = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
            String message = getMessage(msgID, dnString);
            throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                         message, msgID);
          }
        }
        else if ((c == ' ') || (c == ',') || (c == ';'))
        {
          // This denotes the end of the value.
          pos--;
          break;
        }
        else
        {
          int    msgID   = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, dnString, c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
      }
      // At this point, we should have a valid hex string.  Convert it
      // to a byte array and set that as the value of the provided
      // octet string.
      try
      {
        attributeValue.setValue(hexStringToByteArray(
                                     hexString.toString()));
        return pos;
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "parseAttributeValue", e);
        int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_VALUE_DECODE_FAILURE;
        String message = getMessage(msgID, dnString,
                                    String.valueOf(e));
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
    }
    // If the first character is a quotation mark, then the value
    // should continue until the corresponding closing quotation mark.
    else if (c == '"')
    {
      // Keep reading until we find an unescaped closing quotation
      // mark.
      boolean escaped = false;
      StringBuilder valueString = new StringBuilder();
      while (true)
      {
        if (pos >= length)
        {
          // We hit the end of the DN before the closing quote.
          // That's an error.
          int    msgID   = MSGID_ATTR_SYNTAX_DN_UNMATCHED_QUOTE;
          String message = getMessage(msgID, dnString);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        c = dnString.charAt(pos++);
        if (escaped)
        {
          // The previous character was an escape, so we'll take this
          // one no matter what.
          valueString.append(c);
          escaped = false;
        }
        else if (c == '\\')
        {
          // The next character is escaped.  Set a flag to denote
          // this, but don't include the backslash.
          escaped = true;
        }
        else if (c == '"')
        {
          // This is the end of the value.
          break;
        }
        else
        {
          // This is just a regular character that should be in the
          // value.
          valueString.append(c);
        }
      }
      attributeValue.setValue(valueString.toString());
      return pos;
    }
    // Otherwise, use general parsing to find the end of the value.
    else
    {
      boolean escaped;
      StringBuilder valueString = new StringBuilder();
      StringBuilder hexChars    = new StringBuilder();
      if (c == '\\')
      {
        escaped = true;
      }
      else
      {
        escaped = false;
        valueString.append(c);
      }
      // Keep reading until we find an unescaped comma or plus sign or
      // the end of the DN.
      while (true)
      {
        if (pos >= length)
        {
          // This is the end of the DN and therefore the end of the
          // value.  If there are any hex characters, then we need to
          // deal with them accordingly.
          appendHexChars(dnString, valueString, hexChars);
          break;
        }
        c = dnString.charAt(pos++);
        if (escaped)
        {
          // The previous character was an escape, so we'll take this
          // one.  However, this could be a hex digit, and if that's
          // the case then the escape would actually be in front of
          // two hex digits that should be treated as a special
          // character.
          if (isHexDigit(c))
          {
            // It is a hexadecimal digit, so the next digit must be
            // one too.  However, this could be just one in a series
            // of escaped hex pairs that is used in a string
            // containing one or more multi-byte UTF-8 characters so
            // we can't just treat this byte in isolation.  Collect
            // all the bytes together and make sure to take care of
            // these hex bytes before appending anything else to the
            // value.
            if (pos >= length)
            {
              int    msgID   =
                   MSGID_ATTR_SYNTAX_DN_ESCAPED_HEX_VALUE_INVALID;
              String message = getMessage(msgID, dnString);
              throw new DirectoryException(
                             ResultCode.INVALID_DN_SYNTAX, message,
                             msgID);
            }
            else
            {
              char c2 = dnString.charAt(pos++);
              if (isHexDigit(c2))
              {
                hexChars.append(c);
                hexChars.append(c2);
              }
              else
              {
                int    msgID   =
                     MSGID_ATTR_SYNTAX_DN_ESCAPED_HEX_VALUE_INVALID;
                String message = getMessage(msgID, dnString);
                throw new DirectoryException(
                               ResultCode.INVALID_DN_SYNTAX, message,
                               msgID);
              }
            }
          }
          else
          {
            appendHexChars(dnString, valueString, hexChars);
            valueString.append(c);
          }
          escaped = false;
        }
        else if (c == '\\')
        {
          escaped = true;
        }
        else if ((c == ',') || (c == ';'))
        {
          appendHexChars(dnString, valueString, hexChars);
          pos--;
          break;
        }
        else if (c == '+')
        {
          appendHexChars(dnString, valueString, hexChars);
          pos--;
          break;
        }
        else
        {
          appendHexChars(dnString, valueString, hexChars);
          valueString.append(c);
        }
      }
      // Strip off any unescaped spaces that may be at the end of the
      // value.
      if (pos > 2 && dnString.charAt(pos-1) == ' ' &&
           dnString.charAt(pos-2) != '\\')
      {
        int lastPos = valueString.length() - 1;
        while (lastPos > 0)
        {
          if (valueString.charAt(lastPos) == ' ')
          {
            valueString.delete(lastPos, lastPos+1);
            lastPos--;
          }
          else
          {
            break;
          }
        }
      }
      attributeValue.setValue(valueString.toString());
      return pos;
    }
  }
  /**
   * Decodes a hexadecimal string from the provided
   * <CODE>hexChars</CODE> buffer, converts it to a byte array, and
   * then converts that to a UTF-8 string.  The resulting UTF-8 string
   * will be appended to the provided <CODE>valueString</CODE> buffer,
   * and the <CODE>hexChars</CODE> buffer will be cleared.
   *
   * @param  dnString     The DN string that is being decoded.
   * @param  valueString  The buffer containing the value to which the
   *                      decoded string should be appended.
   * @param  hexChars     The buffer containing the hexadecimal
   *                      characters to decode to a UTF-8 string.
   *
   * @throws  DirectoryException  If any problem occurs during the
   *                              decoding process.
   */
  public static void appendHexChars(String dnString,
                                    StringBuilder valueString,
                                    StringBuilder hexChars)
          throws DirectoryException
  {
    try
    {
      byte[] hexBytes = hexStringToByteArray(hexChars.toString());
      valueString.append(new String(hexBytes, "UTF-8"));
      hexChars.delete(0, hexChars.length());
    }
    catch (Exception e)
    {
      assert debugException(CLASS_NAME, "appendHexChars", e);
      int    msgID   = MSGID_ATTR_SYNTAX_DN_ATTR_VALUE_DECODE_FAILURE;
      String message = getMessage(msgID, dnString, String.valueOf(e));
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
  }
  /**
   * Creates a duplicate of this DN that can be modified without
   * impacting this DN.
   *
   * @return  A duplicate of this DN that can be modified without
   *          impacting this DN.
   */
  public DN duplicate()
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    RDN[] rdnCopy = new RDN[numComponents];
    for (int i=0; i < numComponents; i++)
    {
      rdnCopy[i] = rdnComponents[i].duplicate();
    }
    return new DN(rdnCopy);
  }
  /**
   * Indicates whether the provided object is equal to this DN.  In
   * order for the object to be considered equal, it must be a DN with
   * the same number of RDN components and each corresponding RDN
   * component must be equal.
   *
   * @param  o  The object for which to make the determination.
   *
   * @return  <CODE>true</CODE> if the provided object is a DN that is
   *          equal to this DN, or <CODE>false</CODE> if it is not.
   * @param o
   *          The object for which to make the determination.
   * @return <code>true</code> if the provided object is a DN that
   *         is equal to this DN, or <code>false</code> if it is
   *         not.
   */
  public boolean equals(Object o)
  {
  public boolean equals(Object o) {
    assert debugEnter(CLASS_NAME, "equals", String.valueOf(o));
    if (this == o)
    {
    if (this == o) {
      return true;
    }
    if (o == null)
    {
      return false;
    }
    try
    {
      return (normalizedDN.equals(((DN) o).normalizedDN));
    }
    catch (Exception e)
    {
      // This most likely means that the object was null or wasn't a
      // DN.  In either case, it's faster to assume that it is and
      // return false on an exception than to perform the checks to
      // see if it meets the appropriate
      // conditions.
      assert debugException(CLASS_NAME, "equals", e);
    } else if (o instanceof DN) {
      DN other = (DN) o;
      return normalizedDN.equals(other.normalizedDN);
    } else {
      return false;
    }
  }
@@ -2769,8 +628,7 @@
   *
   * @return  The hash code for this DN.
   */
  public int hashCode()
  {
  public int hashCode() {
    assert debugEnter(CLASS_NAME, "hashCode");
    return normalizedDN.hashCode();
@@ -2783,32 +641,12 @@
   *
   * @return  A string representation of this DN.
   */
  public String toString()
  {
  public String toString() {
    assert debugEnter(CLASS_NAME, "toString");
    if (dnString == null)
    {
      if (numComponents == 0)
      {
        dnString = "";
      }
      else
      {
        StringBuilder buffer = new StringBuilder();
        rdnComponents[0].toString(buffer);
        for (int i=1; i < numComponents; i++)
        {
          buffer.append(",");
          rdnComponents[i].toString(buffer);
        }
        dnString = buffer.toString();
      }
    }
    return dnString;
    StringBuilder builder = new StringBuilder();
    toString(builder);
    return builder.toString();
  }
@@ -2817,15 +655,23 @@
   * Appends a string representation of this DN to the provided
   * buffer.
   *
   * @param  buffer  The buffer to which the information should be
   *                 appended.
   * @param buffer
   *          The buffer to which the information should be appended.
   */
  public void toString(StringBuilder buffer)
  {
  public void toString(StringBuilder buffer) {
    assert debugEnter(CLASS_NAME, "toString",
                      "java.lang.StringBuilder");
    buffer.append(toString());
    ensureNotNull(buffer);
    if (numComponents != 0) {
      getRDN(0).toString(buffer);
      for (int i = 1; i < numComponents; i++) {
        buffer.append(",");
        getRDN(i).toString(buffer);
      }
    }
  }
@@ -2835,31 +681,9 @@
   *
   * @return  A normalized string representation of this DN.
   */
  public String toNormalizedString()
  {
  public String toNormalizedString() {
    assert debugEnter(CLASS_NAME, "toNormalizedString");
    if (normalizedDN == null)
    {
      if (numComponents == 0)
      {
        normalizedDN = "";
      }
      else
      {
        StringBuilder buffer = new StringBuilder();
        rdnComponents[0].toNormalizedString(buffer);
        for (int i=1; i < numComponents; i++)
        {
          buffer.append(',');
          rdnComponents[i].toNormalizedString(buffer);
        }
        normalizedDN = buffer.toString();
      }
    }
    return normalizedDN;
  }
@@ -2869,15 +693,16 @@
   * Appends a normalized string representation of this DN to the
   * provided buffer.
   *
   * @param  buffer  The buffer to which the information should be
   *                 appended.
   * @param buffer
   *          The buffer to which the information should be appended.
   */
  public void toNormalizedString(StringBuilder buffer)
  {
  public void toNormalizedString(StringBuilder buffer) {
    assert debugEnter(CLASS_NAME, "toNormalizedString",
                      "java.lang.StringBuilder");
    buffer.append(toNormalizedString());
    ensureNotNull(buffer);
    buffer.append(normalizedDN);
  }
@@ -2887,54 +712,69 @@
   * This order will be first hierarchical (ancestors will come before
   * descendants) and then alphabetical by attribute name(s) and
   * value(s).
   * <p>
   * NOTE: the implementation of this method does not perform a
   * lexicographic comparison of the DN's normalized form. Instead,
   * each individual RDN is compared using ordering matching rules
   * where possible.
   *
   * @param  dn  The DN against which to compare this DN.
   *
   * @param dn
   *          The DN against which to compare this DN.
   * @return  A negative integer if this DN should come before the
   *          provided DN, a positive integer if this DN should come
   *          after the provided DN, or zero if there is no difference
   *          with regard to ordering.
   */
  public int compareTo(DN dn)
  {
    assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(dn));
  public int compareTo(DN dn) {
    assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(this),
        String.valueOf(dn));
    if (equals(dn))
    {
      return 0;
    ensureNotNull(dn);
    int index1 = numComponents - 1;
    int index2 = dn.numComponents - 1;
    while (true) {
      if (index1 >= 0) {
        if (index2 >= 0) {
          int value = getRDN(index1).compareTo(dn.getRDN(index2));
          if (value != 0) {
            return value;
    }
    else if (isNullDN())
    {
      return -1;
    }
    else if (dn.isNullDN())
    {
        } else {
      return 1;
    }
    else if (isAncestorOf(dn))
    {
      } else if (index2 >= 0) {
      return -1;
    }
    else if (isDescendantOf(dn))
    {
      return 1;
    }
    else
    {
      int minComps = Math.min(numComponents, dn.numComponents);
      for (int i=0; i < minComps; i++)
      {
        RDN r1 = rdnComponents[rdnComponents.length-1-i];
        RDN r2 = dn.rdnComponents[dn.rdnComponents.length-1-i];
        int result = r1.compareTo(r2);
        if (result != 0)
        {
          return result;
        }
      }
      } else {
      return 0;
    }
      index1--;
      index2--;
  }
}
  /**
   * Construct the normalized form of this DN.
   *
   * @return Returns the normalized string representation of this DN.
   */
  private String normalize() {
    if (numComponents == 0) {
      return "";
    } else {
      StringBuilder buffer = new StringBuilder();
      getRDN(0).toNormalizedString(buffer);
      for (int i = 1; i < numComponents; i++) {
        buffer.append(',');
        getRDN(i).toNormalizedString(buffer);
      }
      return buffer.toString();
    }
  }
}
opends/src/server/org/opends/server/types/DNComparator.java
File was deleted
opends/src/server/org/opends/server/types/Entry.java
@@ -143,7 +143,7 @@
    if (dn == null)
    {
      this.dn = new DN(new ArrayList<RDN>(0));
      this.dn = DN.nullDN();
    }
    else
    {
@@ -207,7 +207,7 @@
    if (dn == null)
    {
      this.dn = new DN(new ArrayList<RDN>(0));
      this.dn = DN.nullDN();
    }
    else
    {
@@ -2352,8 +2352,10 @@
            }
            // Make sure that all attributes in the RDN are allowed.
            for (AttributeType t : rdn.getAttributeTypes())
            int numAVAs = rdn.getNumValues();
            for (int i = 0; i < numAVAs; i++)
            {
              AttributeType t = rdn.getAttributeType(i);
              if (! nameForm.isRequiredOrOptional(t))
              {
                int msgID = MSGID_ENTRY_SCHEMA_RDN_DISALLOWED_ATTR;
@@ -2408,7 +2410,7 @@
            else
            {
              // Get the DN of the parent entry if possible.
              DN parentDN = dn.getParent();
              DN parentDN = dn.getParentDNInSuffix();
              if (parentDN != null)
              {
                // Get the parent entry and check its structural
@@ -2768,8 +2770,6 @@
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    DN dnCopy = dn.duplicate();
    HashMap<ObjectClass,String> objectClassesCopy =
         new HashMap<ObjectClass,String>(objectClasses);
@@ -2783,7 +2783,7 @@
                  operationalAttributes.size());
    deepCopy(operationalAttributes, operationalAttrsCopy, false);
    return new Entry(dnCopy, objectClassesCopy, userAttrsCopy,
    return new Entry(dn, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
  }
@@ -2806,8 +2806,6 @@
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    DN dnCopy = dn.duplicate();
    HashMap<ObjectClass,String> objectClassesCopy;
    if (typesOnly)
    {
@@ -2838,7 +2836,7 @@
    HashMap<AttributeType,List<Attribute>> operationalAttrsCopy =
         new HashMap<AttributeType,List<Attribute>>(0);
    return new Entry(dnCopy, objectClassesCopy, userAttrsCopy,
    return new Entry(dn, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
  }
@@ -2890,8 +2888,6 @@
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    DN dnCopy = dn.duplicate();
    HashMap<ObjectClass,String> objectClassesCopy =
         new HashMap<ObjectClass,String>(objectClasses.size());
@@ -2903,7 +2899,7 @@
         new HashMap<AttributeType,List<Attribute>>(
                  operationalAttributes.size());
    return new Entry(dnCopy, objectClassesCopy, userAttrsCopy,
    return new Entry(dn, objectClassesCopy, userAttrsCopy,
                     operationalAttrsCopy);
  }
@@ -3216,7 +3212,7 @@
      case SINGLE_LEVEL:
        // The parent DN for this entry must equal the base DN.
        return baseDN.equals(dn.getParent());
        return baseDN.equals(dn.getParentDNInSuffix());
      case WHOLE_SUBTREE:
        // The base DN must be an ancestor of the entry DN.
opends/src/server/org/opends/server/types/LDAPURL.java
@@ -76,7 +76,7 @@
  /**
   * The default base DN that will be used if none is provided.
   */
  public static final DN DEFAULT_BASE_DN = new DN();
  public static final DN DEFAULT_BASE_DN = DN.nullDN();
opends/src/server/org/opends/server/types/RDN.java
@@ -28,18 +28,23 @@
import static org.opends.server.config.ConfigConstants.*;
import static org.opends.server.loggers.Debug.*;
import static org.opends.server.messages.CoreMessages.*;
import static org.opends.server.messages.MessageHandler.getMessage;
import static org.opends.server.messages.SchemaMessages.*;
import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.ensureNotNull;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.TreeMap;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
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.StaticUtils.*;
import org.opends.server.util.StaticUtils;
@@ -47,57 +52,55 @@
 * This class defines a data structure for storing and interacting
 * with the relative distinguished names associated with entries in
 * the Directory Server.
 * <p>
 * All the methods in this class will throw a
 * <code>NullPointerException</code> when provided with
 * <code>null</code> reference parameters unless otherwise stated.
 */
public class RDN
       implements Comparable<RDN>
{
public final class RDN implements Comparable<RDN> {
  // TODO: per-thread cache of common RDNs?
  // TODO: implement pimpl idiom and provide a "singleton"
  // implementation for common case where the RDN only has a single
  // type and value. This would result in less memory being used.
  /**
   * The fully-qualified name of this class for debugging purposes.
   */
  private static final String CLASS_NAME =
       "org.opends.server.types.RDN";
  // The set of attribute types for the elements in this RDN.
  private AttributeType[] attributeTypes;
  // The set of values for the elements in this RDN.
  private AttributeValue[] attributeValues;
  // The number of values for this RDN.
  private int numValues;
  // The string representation of the normalized form of this RDN.
  private String normalizedRDN;
  // The string representation of this RDN.
  private String rdnString;
  private final AttributeType[] attributeTypes;
  // The set of user-provided names for the attributes in this RDN.
  private String[] attributeNames;
  private final String[] attributeNames;
  // The set of values for the elements in this RDN.
  private final AttributeValue[] attributeValues;
  /**
   * The cached normalized string representation of this RDN.
   *
   * This non-final field will default to null. The Java memory model
   * guarantees that it will be initialized to null before being
   * visible to other threads.
   */
  private String normalizedRDN;
  /**
   * Creates a new RDN with the provided information.
   *
   * @param  attributeType   The attribute type for this RDN.
   * @param  attributeValue  The value for this RDN.
   * @param type
   *          The attribute type for this RDN.
   * @param value
   *          The value for this RDN.
   * @return Returns the new RDN.
   */
  public RDN(AttributeType attributeType,
             AttributeValue attributeValue)
  {
    assert debugConstructor(CLASS_NAME, String.valueOf(attributeType),
                            String.valueOf(attributeValue));
    attributeTypes  = new AttributeType[] { attributeType };
    attributeNames  = new String[] { attributeType.getPrimaryName() };
    attributeValues = new AttributeValue[] { attributeValue };
    numValues     = 1;
    rdnString     = null;
    normalizedRDN = null;
  public static RDN create(AttributeType type, AttributeValue value) {
    return create(type, type.getNameOrOID(), value);
  }
@@ -105,85 +108,414 @@
  /**
   * Creates a new RDN with the provided information.
   *
   * @param  attributeType   The attribute type for this RDN.
   * @param  attributeName   The user-provided name for this RDN.
   * @param  attributeValue  The value for this RDN.
   * @param type
   *          The attribute type for this RDN.
   * @param name
   *          The user-provided name for this RDN.
   * @param value
   *          The value for this RDN.
   * @return Returns the new RDN.
   */
  public RDN(AttributeType attributeType, String attributeName,
             AttributeValue attributeValue)
  {
    assert debugConstructor(CLASS_NAME, String.valueOf(attributeType),
                            String.valueOf(attributeName),
                            String.valueOf(attributeValue));
  public static RDN create(AttributeType type, String name,
      AttributeValue value) {
    ensureNotNull(type, name, value);
    attributeTypes  = new AttributeType[] { attributeType };
    attributeNames  = new String[] { attributeName };
    attributeValues = new AttributeValue[] { attributeValue };
    AttributeType[] types = new AttributeType[] { type };
    String[] names = new String[] { name };
    AttributeValue[] values = new AttributeValue[] { value };
    numValues     = 1;
    rdnString     = null;
    normalizedRDN = null;
    return new RDN(types, names, values);
  }
  /**
   * Creates a new RDN with the provided information.  The number of
   * type, name, and value elements must be nonzero and equal.
   * Create a new RDN builder which can be used to incrementally build
   * a new RDN.
   *
   * @param  attributeTypes   The set of attribute types for this RDN.
   * @param  attributeNames   The set of user-provided names for this
   *                          RDN.
   * @param  attributeValues  The set of values for this RDN.
   * @return Returns the new RDN builder.
   */
  public RDN(List<AttributeType> attributeTypes,
             List<String> attributeNames,
             List<AttributeValue> attributeValues)
  {
    assert debugConstructor(CLASS_NAME,
                            String.valueOf(attributeTypes),
                            String.valueOf(attributeNames),
                            String.valueOf(attributeValues));
    this.attributeTypes  = new AttributeType[attributeTypes.size()];
    this.attributeNames  = new String[attributeNames.size()];
    this.attributeValues = new AttributeValue[attributeValues.size()];
    attributeTypes.toArray(this.attributeTypes);
    attributeNames.toArray(this.attributeNames);
    attributeValues.toArray(this.attributeValues);
    numValues     = attributeTypes.size();
    rdnString     = null;
    normalizedRDN = null;
  public static Builder createBuilder() {
    return new Builder();
  }
  /**
   * Creates a new RDN with the provided information.  The number of
   * type, name, and value elements must be nonzero and equal.
   *
   * @param  attributeTypes   The set of attribute types for this RDN.
   * @param  attributeNames   The set of user-provided names for this
   *                          RDN.
   * @param  attributeValues  The set of values for this RDN.
   * This class provides an interface for constructing RDNs
   * incrementally.
   * <p>
   * Typically, an application will construct a new
   * <code>Builder</code> and append attribute value assertions
   * (AVAs) using the <code>append</code> method. When the RDN is
   * fully constructed, it can be retrieved using the
   * <code>getInstance</code> method.
   */
  public RDN(AttributeType[] attributeTypes, String[] attributeNames,
             AttributeValue[] attributeValues)
  {
    assert debugConstructor(CLASS_NAME,
                            String.valueOf(attributeTypes),
                            String.valueOf(attributeNames),
                            String.valueOf(attributeValues));
  public static final class Builder {
    // The list of attribute types.
    private List<AttributeType> attributeTypes;
    this.numValues       = attributeTypes.length;
    this.attributeTypes  = attributeTypes;
    this.attributeNames  = attributeNames;
    this.attributeValues = attributeValues;
    // The list of user-provided attribute names.
    private List<String> attributeNames;
    rdnString     = null;
    normalizedRDN = null;
    // The list of attribute values.
    private List<AttributeValue> attributeValues;
    /**
     * Create the new empty RDN builder.
     */
    private Builder() {
      clear();
    }
    /**
     * Appends the provided attribute value assertion to the RDN.
     *
     * @param type
     *          The attribute type.
     * @param value
     *          The attribute value.
     * @throws IllegalArgumentException
     *           If the RDN being constructed already contains an
     *           attribute value assertion for this attribute type.
     */
    public void append(AttributeType type, AttributeValue value)
        throws IllegalArgumentException {
      append(type, type.getNameOrOID(), value);
    }
    /**
     * Appends the provided attribute value assertion to the RDN.
     *
     * @param type
     *          The attribute type.
     * @param name
     *          The user-provided attribute name.
     * @param value
     *          The attribute value.
     * @throws IllegalArgumentException
     *           If the RDN being constructed already contains an
     *           attribute value assertion for this attribute type.
     */
    public void append(AttributeType type, String name,
        AttributeValue value) throws IllegalArgumentException {
      ensureNotNull(type, name, value);
      if (attributeTypes.contains(type)) {
        throw new IllegalArgumentException(
            "Builder already contains the attribute type "
                + type.getNameOrOID());
      }
      attributeTypes.add(type);
      attributeNames.add(name);
      attributeValues.add(value);
    }
    /**
     * Parses an RDN from the provided string starting at the
     * specified location, appending any AVAs to this RDN builder.
     * <p>
     * This method is package visible so that it can be used by
     * the DN decoder. It is not intended for use elsewhere.
     *
     * @param s
     *          The string to be parsed.
     * @param pos
     *          The position of the first character in the string to
     *          parse.
     * @param allowEmpty
     *          Flag indicating whether or not the parsed RDN can be
     *          empty or not.
     * @return Returns <code>-1</code> if decoding was successful
     *         and parsing consumed the remainder of the string
     *         (including trailing space), or the position of the next
     *         RDN separator character (i.e. a ',' or ';').
     * @throws DirectoryException
     *           If it was not possible to parse a valid RDN from the
     *           provided string.
     */
    int parse(String s, int pos, boolean allowEmpty)
        throws DirectoryException {
      assert debugEnter(CLASS_NAME, "parse", String.valueOf(s),
          String.valueOf(pos));
      // There must be at least one AVA.
      int count = attributeTypes.size();
      pos = parseAVA(s, pos);
      if (pos == -1 && !allowEmpty) {
        if (count == attributeTypes.size()) {
          // Nothing was parsed.
          int msgID = MSGID_RDN_DECODE_NULL;
          String message = getMessage(msgID);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
      }
      // Parse any remaining AVAs.
      while (pos != -1 && s.charAt(pos) == '+') {
        count = attributeTypes.size();
        pos = parseAVA(s, pos + 1);
        if (pos == -1 && count == attributeTypes.size()) {
          // Nothing was parsed.
          int msgID = MSGID_RDN_UNEXPECTED_COMMA;
          String message = getMessage(msgID, s, pos);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
      }
      return pos;
    }
    /**
     * Parse a single AVA.
     *
     * @param s
     *          The string to be parsed.
     * @param pos
     *          The position of the first character in the string to
     *          parse.
     * @return Returns <code>-1</code> if decoding was successful
     *         and parsing consumed the remainder of the possibly
     *         empty string (including trailing space), or the
     *         position of the next AVA separator character (i.e. a
     *         '+', ',' or ';').
     * @throws DirectoryException
     *           If it was not possible to parse a valid AVA from the
     *           provided string.
     */
    private int parseAVA(String s, int pos)
        throws DirectoryException {
      int length = s.length();
      // Skip over any spaces that may follow it
      // before the next attribute name.
      char c;
      while ((pos < length) && ((c = s.charAt(pos)) == ' ')) {
        pos++;
      }
      // Reached the end of the string - let the caller handle this.
      if (pos >= length) {
        return -1;
      }
      // Parse the attribute name.
      StringBuilder attributeName = new StringBuilder();
      pos = parseAttributeName(s, pos, attributeName);
      // Make sure we're not at the end of the RDN.
      if (pos >= length) {
        int msgID = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
        String message = getMessage(msgID, s, attributeName
            .toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
      // Skip over any spaces between the attribute name and the
      // equal sign.
      c = s.charAt(pos);
      while (c == ' ') {
        pos++;
        if (pos >= length) {
          // This means that we hit the end of the string before
          // finding a '='. This is illegal because there is no
          // attribute-value separator.
          int msgID = MSGID_ATTR_SYNTAX_DN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, s, attributeName
              .toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        } else {
          c = s.charAt(pos);
        }
      }
      // The next character must be an equal sign.
      if (c == '=') {
        pos++;
      } else {
        int msgID = MSGID_ATTR_SYNTAX_DN_NO_EQUAL;
        String message = getMessage(msgID, s, attributeName
            .toString(), c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
      // Skip over any spaces after the equal sign.
      while ((pos < length) && ((c = s.charAt(pos)) == ' ')) {
        pos++;
      }
      // If we are at the end of the RDN string, then that must mean
      // that the attribute value was empty. This will probably
      // never happen in a real-world environment, but technically
      // isn't illegal. If it does happen, then go ahead and return
      // the RDN.
      if (pos >= length) {
        String name = attributeName.toString();
        String lowerName = toLowerCase(name);
        AttributeType attrType = DirectoryServer
            .getAttributeType(lowerName);
        if (attrType == null) {
          // This must be an attribute type that we don't know
          // about.
          // In that case, we'll create a new attribute using the
          // default syntax. If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        AttributeValue value = new AttributeValue(
            new ASN1OctetString(), new ASN1OctetString());
        append(attrType, name, value);
        return -1;
      }
      // Parse the value for this RDN component.
      ByteString parsedValue = new ASN1OctetString();
      pos = parseAttributeValue(s, pos, parsedValue);
      // Update the RDN to include the new attribute/value.
      String name = attributeName.toString();
      String lowerName = toLowerCase(name);
      AttributeType attrType = DirectoryServer
          .getAttributeType(lowerName);
      if (attrType == null) {
        // This must be an attribute type that we don't know about.
        // In that case, we'll create a new attribute using the
        // default syntax. If this is a problem, it will be caught
        // later either by not finding the target entry or by not
        // allowing the entry to be added.
        attrType = DirectoryServer.getDefaultAttributeType(name);
      }
      AttributeValue value = new AttributeValue(attrType,
          parsedValue);
      append(attrType, name, value);
      // Skip over any spaces that might be after the attribute
      // value.
      while ((pos < length) && ((c = s.charAt(pos)) == ' ')) {
        pos++;
      }
      // If we're at the end of the string, then return the RDN.
      if (pos >= length) {
        return -1;
      }
      // If the next character is a comma or semicolon, then that is
      // not allowed. It would be legal for a DN but not an RDN.
      if ((c == ',') || (c == ';')) {
        return pos;
      }
      // If the next character is anything but a plus sign, then it
      // is illegal.
      if (c != '+') {
        int msgID = MSGID_ATTR_SYNTAX_DN_INVALID_CHAR;
        String message = getMessage(msgID, s, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
      return pos;
    }
    /**
     * Removes all the attribute value assertions from this RDN
     * builder.
     */
    public void clear() {
      attributeTypes = new ArrayList<AttributeType>(3);
      attributeValues = new ArrayList<AttributeValue>(3);
      attributeNames = new ArrayList<String>(3);
    }
    /**
     * Returns <code>true</code> if this RDN builder is empty.
     *
     * @return Returns <code>true</code> if this RDN builder is
     *         empty.
     */
    public boolean isEmpty() {
      return attributeTypes.isEmpty();
    }
    /**
     * Creates a new RDN instance based on the current contents of
     * this RDN builder. Subsequent changes to this RDN builder do not
     * affect the contents of the returned <code>RDN</code>.
     *
     * @return Returns a new RDN instance based on the current
     *         contents of this RDN builder.
     * @throws IllegalStateException
     *           If a new RDN could not be created because this RDN
     *           builder is emtpy.
     */
    public RDN getInstance() throws IllegalStateException {
      int sz = attributeTypes.size();
      if (sz == 0) {
        throw new IllegalStateException("RDN builder is empty");
      }
      AttributeType[] types = new AttributeType[sz];
      String[] names = new String[sz];
      AttributeValue[] values = new AttributeValue[sz];
      attributeTypes.toArray(types);
      attributeNames.toArray(names);
      attributeValues.toArray(values);
      return new RDN(types, names, values);
    }
  }
  /**
   * Creates a new RDN with the provided information.
   *
   * @param types
   *          The attribute types for this RDN.
   * @param names
   *          The user-provided names for this RDN.
   * @param values
   *          The values for this RDN.
   */
  private RDN(AttributeType[] types, String[] names,
      AttributeValue[] values) {
    assert debugConstructor(CLASS_NAME, String.valueOf(types), String
        .valueOf(names), String.valueOf(values));
    this.attributeTypes = types;
    this.attributeNames = names;
    this.attributeValues = values;
  }
@@ -195,26 +527,86 @@
   * @return  The number of attribute-value pairs contained in this
   *          RDN.
   */
  public int getNumValues()
  {
  public int getNumValues() {
    assert debugEnter(CLASS_NAME, "getNumValues");
    return numValues;
    return attributeTypes.length;
  }
  /**
   * Retrieves the set of attribute types for this RDN.  The returned
   * array must not be modified by the caller.
   * Retrieves the attribute type at the specified AVA in this RDN.
   *
   * @return  The set of attribute types for this RDN.
   * @param index
   *          The index of the AVA in this RDN.
   * @return Returns the attribute type at the specified AVA in this
   *         RDN.
   * @throws IndexOutOfBoundsException
   *           If <code>index</code> is out of range
   *           <code>(index < 0 || index >= getNumValues()</code>.
   */
  public AttributeType[] getAttributeTypes()
  {
    assert debugEnter(CLASS_NAME, "getAttributeTypes");
  public AttributeType getAttributeType(int index)
      throws IndexOutOfBoundsException {
    assert debugEnter(CLASS_NAME, "getAttributeType");
    return attributeTypes;
    if (index < 0 || index >= attributeTypes.length) {
      throw new IndexOutOfBoundsException("Index: " + index
          + ", Size: " + attributeTypes.length);
    }
    return attributeTypes[index];
  }
  /**
   * Retrieves the user-defined attribute name at the specified AVA in
   * this RDN.
   *
   * @param index
   *          The index of the AVA in this RDN.
   * @return Returns the user-defined attribute name at the specified
   *         AVA in this RDN.
   * @throws IndexOutOfBoundsException
   *           If <code>index</code> is out of range
   *           <code>(index < 0 || index >= getNumValues()</code>.
   */
  public String getAttributeName(int index)
      throws IndexOutOfBoundsException {
    assert debugEnter(CLASS_NAME, "getAttributeName");
    if (index < 0 || index >= attributeTypes.length) {
      throw new IndexOutOfBoundsException("Index: " + index
          + ", Size: " + attributeTypes.length);
    }
    return attributeNames[index];
  }
  /**
   * Retrieves the attribute value at the specified AVA in this RDN.
   * <p>
   * Applications <b>must not</b> modify the contents of the returned
   * attribute value.
   *
   * @param index
   *          The index of the AVA in this RDN.
   * @return Returns the attribute value at the specified AVA in this
   *         RDN.
   * @throws IndexOutOfBoundsException
   *           If <code>index</code> is out of range
   *           <code>(index < 0 || index >= getNumValues()</code>.
   */
  public AttributeValue getAttributeValue(int index)
      throws IndexOutOfBoundsException {
    assert debugEnter(CLASS_NAME, "getAttributeValue");
    if (index < 0 || index >= attributeTypes.length) {
      throw new IndexOutOfBoundsException("Index: " + index
          + ", Size: " + attributeTypes.length);
    }
    return attributeValues[index];
  }
@@ -222,21 +614,19 @@
  /**
   * Indicates whether this RDN includes the specified attribute type.
   *
   * @param  attributeType  The attribute type for which to make the
   *                        determination.
   *
   * @return  <CODE>true</CODE> if the RDN includes the specified
   *          attribute type, or <CODE>false</CODE> if not.
   * @param attributeType
   *          The attribute type for which to make the determination.
   * @return <code>true</code> if the RDN includes the specified
   *         attribute type, or <code>false</code> if not.
   */
  public boolean hasAttributeType(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "hasAttributeType",
                      String.valueOf(attributeType));
  public boolean hasAttributeType(AttributeType attributeType) {
    assert debugEnter(CLASS_NAME, "hasAttributeType", String
        .valueOf(attributeType));
    for (AttributeType t : attributeTypes)
    {
      if (t.equals(attributeType))
      {
    ensureNotNull(attributeType);
    for (AttributeType t : attributeTypes) {
      if (t.equals(attributeType)) {
        return true;
      }
    }
@@ -247,91 +637,28 @@
  /**
   * Indicates whether this RDN includes the specified attribute type.
   *
   * @param  lowerName  The name or OID for the attribute type for
   *                    which to make the determination, formatted in
   *                    all lowercase characters.
   *
   * @return  <CODE>true</CODE> if the RDN includes the specified
   *          attribute type, or <CODE>false</CODE> if not.
   */
  public boolean hasAttributeType(String lowerName)
  {
    assert debugEnter(CLASS_NAME, "hasAttributeType",
                      String.valueOf(lowerName));
    for (AttributeType t : attributeTypes)
    {
      if (t.hasNameOrOID(lowerName))
      {
        return true;
      }
    }
    for (String s : attributeNames)
    {
      if (s.equalsIgnoreCase(lowerName))
      {
        return true;
      }
    }
    return false;
  }
  /**
   * Retrieves the set of user-provided names for this RDN.  The
   * returned array must not be modified by the caller.
   *
   * @return  The set of user-provided names for this RDN.
   */
  public String[] getAttributeNames()
  {
    assert debugEnter(CLASS_NAME, "getAttributeNames");
    return attributeNames;
  }
  /**
   * Retrieves the set of attribute values for this RDN.  The returned
   * list must not be modified by the caller.
   *
   * @return  The set of attribute values for this RDN.
   */
  public AttributeValue[] getAttributeValues()
  {
    assert debugEnter(CLASS_NAME, "getAttributeValues");
    return attributeValues;
  }
  /**
   * Retrieves the attribute value that is associated with the
   * specified attribute type.
   * <p>
   * Applications <b>must not</b> modify the contents of the returned
   * attribute value.
   *
   * @param  attributeType  The attribute type for which to retrieve
   *                        the corresponding value.
   *
   * @param attributeType
   *          The attribute type for which to retrieve the
   *          corresponding value.
   * @return  The value for the requested attribute type, or
   *          <CODE>null</CODE> if the specified attribute type is not
   *          present in the RDN.
   *         <code>null</code> if the specified attribute type is
   *         not present in the RDN.
   */
  public AttributeValue getAttributeValue(AttributeType attributeType)
  {
    assert debugEnter(CLASS_NAME, "getAttributeValue",
                      String.valueOf(attributeType));
  public AttributeValue getAttributeValue(
      AttributeType attributeType) {
    assert debugEnter(CLASS_NAME, "getAttributeValue", String
        .valueOf(attributeType));
    for (int i=0; i < numValues; i++)
    {
      if (attributeTypes[i].equals(attributeType))
      {
    ensureNotNull(attributeType);
    for (int i = 0; i < attributeTypes.length; i++) {
      if (attributeTypes[i].equals(attributeType)) {
        return attributeValues[i];
      }
    }
@@ -344,252 +671,57 @@
  /**
   * Indicates whether this RDN is multivalued.
   *
   * @return  <CODE>true</CODE> if this RDN is multivalued, or
   *          <CODE>false</CODE> if not.
   * @return <code>true</code> if this RDN is multivalued, or
   *         <code>false</code> if not.
   */
  public boolean isMultiValued()
  {
  public boolean isMultiValued() {
    assert debugEnter(CLASS_NAME, "isMultiValued");
    return (numValues > 1);
    return (attributeTypes.length > 1);
  }
  /**
   * Indicates whether this RDN contains the specified type-value
   * pair.
   * Returns an <code>RDN</code> object holding the value of the
   * specified <code>String</code>. The argument is interpreted as
   * representing the LDAP string representation of an RDN.
   * <p>
   * This method is identical to {@link #decode(String)}.
   *
   * @param  type   The attribute type for which to make the
   *                determination.
   * @param  value  The value for which to make the determination.
   *
   * @return  <CODE>true</CODE> if this RDN contains the specified
   *          attribute value, or <CODE>false</CODE> if not.
   * @param s
   *          The string to be parsed.
   * @return Returns a <code>RDN</code> holding the value
   *         represented by the <code>string</code> argument.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           string as a RDN.
   */
  public boolean hasValue(AttributeType type, AttributeValue value)
  {
    assert debugEnter(CLASS_NAME, "hasValue", String.valueOf(type),
                      String.valueOf(value));
    for (int i=0; i < numValues; i++)
    {
      if (attributeTypes[i].equals(type) &&
          attributeValues[i].equals(value))
      {
        return true;
      }
    }
    return false;
  public static RDN valueOf(String s) throws DirectoryException {
    return decode(s);
  }
  /**
   * Adds the provided type-value pair from this RDN.
   * Decodes the provided ASN.1 octet string as a RDN.
   *
   * @param  type   The attribute type of the pair to add.
   * @param  name   The user-provided name of the pair to add.
   * @param  value  The attribute value of the pair to add.
   *
   * @return  <CODE>true</CODE> if the type-value pair was added to
   *          this RDN, or <CODE>false</CODE> if it was not (e.g., it
   *          was already present).
   * @param rdnString
   *          The ASN.1 octet string to decode as a RDN.
   * @return The decoded RDN.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           ASN.1 octet string as a RDN.
   */
  public boolean addValue(AttributeType type, String name,
                          AttributeValue value)
  {
    assert debugEnter(CLASS_NAME, "addValue", String.valueOf(type),
                      String.valueOf(name), String.valueOf(value));
  public static RDN decode(ByteString rdnString)
      throws DirectoryException {
    assert debugEnter(CLASS_NAME, "decode",
        String.valueOf(rdnString));
    for (int i=0; i < numValues; i++)
    {
      if (attributeTypes[i].equals(type) &&
          attributeValues[i].equals(value))
      {
        return false;
      }
    }
    ensureNotNull(rdnString);
    numValues++;
    AttributeType[] newTypes = new AttributeType[numValues];
    System.arraycopy(attributeTypes, 0, newTypes, 0,
                     attributeTypes.length);
    newTypes[attributeTypes.length] = type;
    attributeTypes = newTypes;
    String[] newNames = new String[numValues];
    System.arraycopy(attributeNames, 0, newNames, 0,
                     attributeNames.length);
    newNames[attributeNames.length] = name;
    attributeNames = newNames;
    AttributeValue[] newValues = new AttributeValue[numValues];
    System.arraycopy(attributeValues, 0, newValues, 0,
                     attributeValues.length);
    newValues[attributeValues.length] = value;
    attributeValues = newValues;
    rdnString     = null;
    normalizedRDN = null;
    return true;
  }
  /**
   * Removes the provided type-value pair from this RDN.
   *
   * @param  type   The attribute type of the pair to remove.
   * @param  value  The attribute value of the pair to remove.
   *
   * @return  <CODE>true</CODE> if the type-value pair was found and
   *          removed from this RDN, or <CODE>false</CODE> if it was
   *          not.
   */
  public boolean removeValue(AttributeType type, AttributeValue value)
  {
    assert debugEnter(CLASS_NAME, "removeValue", String.valueOf(type),
                      String.valueOf(value));
    for (int i=0; i < numValues; i++)
    {
      if (attributeTypes[i].equals(type) &&
          attributeValues[i].equals(value))
      {
        numValues--;
        if (numValues == 0)
        {
          attributeTypes  = new AttributeType[0];
          attributeNames  = new String[0];
          attributeValues = new AttributeValue[0];
        }
        else if (i == 0)
        {
          AttributeType[] newTypes = new AttributeType[numValues];
          System.arraycopy(attributeTypes, 1, newTypes, 0, numValues);
          attributeTypes = newTypes;
          String[] newNames = new String[numValues];
          System.arraycopy(attributeNames, 1, newNames, 0, numValues);
          attributeNames = newNames;
          AttributeValue[] newValues = new AttributeValue[numValues];
          System.arraycopy(attributeValues, 1, newValues, 0,
                           numValues);
          attributeValues = newValues;
        }
        else if (i == numValues)
        {
          AttributeType[] newTypes = new AttributeType[numValues];
          System.arraycopy(attributeTypes, 0, newTypes, 0, numValues);
          attributeTypes = newTypes;
          String[] newNames = new String[numValues];
          System.arraycopy(attributeNames, 0, newNames, 0, numValues);
          attributeNames = newNames;
          AttributeValue[] newValues = new AttributeValue[numValues];
          System.arraycopy(attributeValues, 0, newValues, 0,
                           numValues);
          attributeValues = newValues;
        }
        else
        {
          int remaining = numValues - i;
          AttributeType[] newTypes = new AttributeType[numValues];
          System.arraycopy(attributeTypes, 0, newTypes, 0, i);
          System.arraycopy(attributeTypes, i+1, newTypes, i,
                           remaining);
          attributeTypes = newTypes;
          String[] newNames = new String[numValues];
          System.arraycopy(attributeNames, 0, newNames, 0, i);
          System.arraycopy(attributeNames, i+1, newNames, i,
                           remaining);
          attributeNames = newNames;
          AttributeValue[] newValues = new AttributeValue[numValues];
          System.arraycopy(attributeValues, 0, newValues, 0, i);
          System.arraycopy(attributeValues, i+1, newValues, i,
                           remaining);
          attributeValues = newValues;
        }
        rdnString     = null;
        normalizedRDN = null;
        return true;
      }
    }
    return false;
  }
  /**
   * Replaces the set of values for this RDN with the provided
   * name-value pair.
   *
   * @param  type   The attribute type for this RDN.
   * @param  name   The user-provided name for this RDN.
   * @param  value  The attribute value for this RDN.
   */
  public void replaceValues(AttributeType type, String name,
                            AttributeValue value)
  {
    assert debugEnter(CLASS_NAME, "replaceValues",
                      String.valueOf(type), String.valueOf(name),
                      String.valueOf(value));
    attributeTypes  = new AttributeType[] { type };
    attributeNames  = new String[] { name };
    attributeValues = new AttributeValue[] { value };
    numValues     = 1;
    rdnString     = null;
    normalizedRDN = null;
  }
  /**
   * Replaces the set of values for this RDN with the provided set of
   * name-value pairs.  The number of elements in each list must be
   * equal and greater than one.
   *
   * @param  attributeTypes   The set of attribute types for this RDN.
   * @param  attributeNames   The set of user-provided names for this
   *                          RDN.
   * @param  attributeValues  The set of values for this RDN.
   */
  public void replaceValues(ArrayList<AttributeType> attributeTypes,
                            ArrayList<String> attributeNames,
                            ArrayList<AttributeValue> attributeValues)
  {
    assert debugEnter(CLASS_NAME, "replaceValues",
                      String.valueOf(attributeTypes),
                      String.valueOf(attributeNames),
                      String.valueOf(attributeValues));
    this.attributeTypes = new AttributeType[attributeTypes.size()];
    attributeTypes.toArray(this.attributeTypes);
    this.attributeNames = new String[attributeNames.size()];
    attributeNames.toArray(this.attributeNames);
    this.attributeValues = new AttributeValue[attributeValues.size()];
    attributeValues.toArray(this.attributeValues);
    numValues     = attributeTypes.size();
    rdnString     = null;
    normalizedRDN = null;
    // Use string-based decoder.
    return decode(rdnString.stringValue());
  }
@@ -597,477 +729,78 @@
  /**
   * Decodes the provided string as an RDN.
   *
   * @param  rdnString  The string to decode as an RDN.
   *
   * @param rdnString
   *          The string to decode as an RDN.
   * @return  The decoded RDN.
   *
   * @throws  DirectoryException  If a problem occurs while trying to
   *                              decode the provided string as a RDN.
   * @throws DirectoryException
   *           If a problem occurs while trying to decode the provided
   *           string as a RDN.
   */
  public static RDN decode(String rdnString)
         throws DirectoryException
  {
      throws DirectoryException {
    assert debugEnter(CLASS_NAME, "decode",
                      String.valueOf(rdnString));
    ensureNotNull(rdnString);
    // A null or empty RDN is not acceptable.
    if (rdnString == null)
    {
      int    msgID   = MSGID_RDN_DECODE_NULL;
      String message = getMessage(msgID);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // Use an RDN builder to parse the string.
    Builder builder = createBuilder();
    int pos = builder.parse(rdnString, 0, false);
    int length = rdnString.length();
    if (length == 0)
    {
      int    msgID   = MSGID_RDN_DECODE_NULL;
      String message = getMessage(msgID);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // Iterate through the RDN string.  The first thing to do is to
    // get rid of any leading spaces.
    int pos = 0;
    char c = rdnString.charAt(pos);
    while (c == ' ')
    {
      pos++;
      if (pos == length)
      {
        // This means that the RDN was completely comprised of spaces,
        // which is not valid.
        int    msgID   = MSGID_RDN_DECODE_NULL;
        String message = getMessage(msgID);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      else
      {
        c = rdnString.charAt(pos);
      }
    }
    // We know that it's not an empty RDN, so we can do the real
    // processing.  First, parse the attribute name.  We can borrow
    // the DN code for this.
    boolean allowExceptions =
         DirectoryServer.allowAttributeNameExceptions();
    StringBuilder attributeName = new StringBuilder();
    pos = DN.parseAttributeName(rdnString, pos, attributeName,
                                allowExceptions);
    // Make sure that we're not at the end of the RDN string because
    // that would be invalid.
    if (pos >= length)
    {
      int    msgID   = MSGID_RDN_END_WITH_ATTR_NAME;
      String message = getMessage(msgID,
                            rdnString, attributeName.toString());
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // Skip over any spaces between the attribute name and its value.
    c = rdnString.charAt(pos);
    while (c == ' ')
    {
      pos++;
      if (pos >= length)
      {
        // This means that we hit the end of the string before
        // finding a '='.  This is illegal because there is no
        // attribute-value separator.
        int    msgID   = MSGID_RDN_END_WITH_ATTR_NAME;
        String message = getMessage(msgID,
                              rdnString, attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      else
      {
        c = rdnString.charAt(pos);
      }
    }
    // The next character must be an equal sign.  If it is not, then
    // that's an error.
    if (c == '=')
    {
      pos++;
    }
    else
    {
      int    msgID   = MSGID_RDN_NO_EQUAL;
      String message = getMessage(msgID, rdnString,
                                  attributeName.toString(), c, pos);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // Skip over any spaces between the equal sign and the value.
    while ((pos < length) && ((c = rdnString.charAt(pos)) == ' '))
    {
      pos++;
    }
    // If we are at the end of the RDN string, then that must mean
    // that the attribute value was empty.  This will probably never
    // happen in a real-world environment, but technically isn't
    // illegal.  If it does happen, then go ahead and return the RDN.
    if (pos >= length)
    {
      String        name      = attributeName.toString();
      String        lowerName = toLowerCase(name);
      AttributeType attrType  =
           DirectoryServer.getAttributeType(lowerName);
      if (attrType == null)
      {
        // This must be an attribute type that we don't know about.
        // In that case, we'll create a new attribute using the
        // default syntax.  If this is a problem, it will be caught
        // later either by not finding the target entry or by not
        // allowing the entry to be added.
        attrType = DirectoryServer.getDefaultAttributeType(name);
      }
      AttributeValue value = new AttributeValue(new ASN1OctetString(),
                                     new ASN1OctetString());
      return new RDN(attrType, name, value);
    }
    // Parse the value for this RDN component.  This can be done using
    // the DN code.
    ByteString parsedValue = new ASN1OctetString();
    pos = DN.parseAttributeValue(rdnString, pos, parsedValue);
    // Create the new RDN with the provided information.  However,
    // don't return it yet because this could be a multi-valued RDN.
    String name            = attributeName.toString();
    String lowerName       = toLowerCase(name);
    AttributeType attrType =
         DirectoryServer.getAttributeType(lowerName);
    if (attrType == null)
    {
      // This must be an attribute type that we don't know about.
      // In that case, we'll create a new attribute using the default
      // syntax.  If this is a problem, it will be caught later either
      // by not finding the target entry or by not allowing the entry
      // to be added.
      attrType = DirectoryServer.getDefaultAttributeType(name);
    }
    AttributeValue value = new AttributeValue(attrType, parsedValue);
    RDN rdn = new RDN(attrType, name, value);
    // Skip over any spaces that might be after the attribute value.
    while ((pos < length) && ((c = rdnString.charAt(pos)) == ' '))
    {
      pos++;
    }
    // Most likely, this is the end of the RDN.  If so, then return
    // it.
    if (pos >= length)
    {
      return rdn;
    }
    // If the next character is a comma or semicolon, then that is not
    // allowed.  It would be legal for a DN but not an RDN.
    if ((c == ',') || (c == ';'))
    {
    // Make sure that the string did not contain any trailing RDNs.
    if (pos != -1) {
      int    msgID   = MSGID_RDN_UNEXPECTED_COMMA;
      String message = getMessage(msgID, rdnString, pos);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // If the next character is anything but a plus sign, then it is
    // illegal.
    if (c != '+')
    {
      int    msgID   = MSGID_RDN_ILLEGAL_CHARACTER;
      String message = getMessage(msgID, rdnString, c, pos);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                   message, msgID);
    }
    // If we have gotten here, then it is a multi-valued RDN.  Parse
    // the remaining attribute/value pairs and add them to the RDN
    // that we've already created.
    while (true)
    {
      // Skip over the plus sign and any spaces that may follow it
      // before the next attribute name.
      pos++;
      while ((pos < length) && ((c = rdnString.charAt(pos)) == ' '))
      {
        pos++;
      }
      // Parse the attribute name.
      attributeName = new StringBuilder();
      pos = DN.parseAttributeName(rdnString, pos, attributeName,
                                  allowExceptions);
      // Make sure we're not at the end of the RDN.
      if (pos >= length)
      {
        int    msgID   = MSGID_RDN_END_WITH_ATTR_NAME;
        String message = getMessage(msgID, rdnString,
                                    attributeName.toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces between the attribute name and the equal
      // sign.
      c = rdnString.charAt(pos);
      while (c == ' ')
      {
        pos++;
        if (pos >= length)
        {
          // This means that we hit the end of the string before
          // finding a '='.  This is illegal because there is no
          // attribute-value separator.
          int    msgID   = MSGID_RDN_END_WITH_ATTR_NAME;
          String message = getMessage(msgID, rdnString,
                                      attributeName.toString());
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                       message, msgID);
        }
        else
        {
          c = rdnString.charAt(pos);
        }
      }
      // The next character must be an equal sign.
      if (c == '=')
      {
        pos++;
      }
      else
      {
        int    msgID   = MSGID_RDN_NO_EQUAL;
        String message = getMessage(msgID, rdnString,
                                    attributeName.toString(), c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // Skip over any spaces after the equal sign.
      while ((pos < length) && ((c = rdnString.charAt(pos)) == ' '))
      {
        pos++;
      }
      // If we are at the end of the RDN string, then that must mean
      // that the attribute value was empty.  This will probably never
      // happen in a real-world environment, but technically isn't
      // illegal.  If it does happen, then go ahead and return the
      // RDN.
      if (pos >= length)
      {
        name      = attributeName.toString();
        lowerName = toLowerCase(name);
        attrType  = DirectoryServer.getAttributeType(lowerName);
        if (attrType == null)
        {
          // This must be an attribute type that we don't know about.
          // In that case, we'll create a new attribute using the
          // default syntax.  If this is a problem, it will be caught
          // later either by not finding the target entry or by not
          // allowing the entry to be added.
          attrType = DirectoryServer.getDefaultAttributeType(name);
        }
        value = new AttributeValue(new ASN1OctetString(),
                                   new ASN1OctetString());
        rdn.addValue(attrType, name, value);
        return rdn;
      }
      // Parse the value for this RDN component.
      parsedValue = new ASN1OctetString();
      pos = DN.parseAttributeValue(rdnString, pos, parsedValue);
      // Update the RDN to include the new attribute/value.
      name            = attributeName.toString();
      lowerName       = toLowerCase(name);
      attrType = DirectoryServer.getAttributeType(lowerName);
      if (attrType == null)
      {
        // This must be an attribute type that we don't know about.
        // In that case, we'll create a new attribute using the
        // default syntax.  If this is a problem, it will be caught
        // later either by not finding the target entry or by not
        // allowing the entry to be added.
        attrType = DirectoryServer.getDefaultAttributeType(name);
      }
      value = new AttributeValue(attrType, parsedValue);
      rdn.addValue(attrType, name, value);
      // Skip over any spaces that might be after the attribute value.
      while ((pos < length) && ((c = rdnString.charAt(pos)) == ' '))
      {
        pos++;
      }
      // If we're at the end of the string, then return the RDN.
      if (pos >= length)
      {
        return rdn;
      }
      // If the next character is a comma or semicolon, then that is
      // not allowed.  It would be legal for a DN but not an RDN.
      if ((c == ',') || (c == ';'))
      {
        int    msgID   = MSGID_RDN_UNEXPECTED_COMMA;
        String message = getMessage(msgID, rdnString, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
      // If the next character is anything but a plus sign, then it is
      // illegal.
      if (c != '+')
      {
        int    msgID   = MSGID_RDN_ILLEGAL_CHARACTER;
        String message = getMessage(msgID, rdnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
                                     message, msgID);
      }
    }
  }
  /**
   * Creates a duplicate of this RDN that can be modified without
   * impacting this RDN.
   *
   * @return  A duplicate of this RDN that can be modified without
   *          impacting this RDN.
   */
  public RDN duplicate()
  {
    assert debugEnter(CLASS_NAME, "duplicate");
    AttributeType[] newTypes = new AttributeType[numValues];
    System.arraycopy(attributeTypes, 0, newTypes, 0, numValues);
    String[] newNames = new String[numValues];
    System.arraycopy(attributeNames, 0, newNames, 0, numValues);
    AttributeValue[] newValues = new AttributeValue[numValues];
    System.arraycopy(attributeValues, 0, newValues, 0, numValues);
    return new RDN(newTypes, newNames, newValues);
    // Return the parsed RDN instance.
    return builder.getInstance();
  }
  /**
   * Indicates whether the provided object is equal to this RDN.  It
   * will only be considered equal if it is an RDN object that
   * contains the same number of elements in the same order with the
   * same types and normalized values.
   * will only be considered equal if it is an RDN object containing
   * the same attribute value assertions as this RDN (the order does
   * not matter).
   *
   * @param  o  The object for which to make the determination.
   *
   * @return  <CODE>true</CODE> if it is determined that the provided
   *          object is equal to this RDN, or <CODE>false</CODE> if
   * @param o
   *          The object for which to make the determination.
   * @return <code>true</code> if it is determined that the provided
   *         object is equal to this RDN, or <code>false</code> if
   *          not.
   */
  public boolean equals(Object o)
  {
  public boolean equals(Object o) {
    assert debugEnter(CLASS_NAME, "equals", String.valueOf(o));
    if (this == o)
    {
    if (this == o) {
      return true;
    }
    } else if (o instanceof RDN) {
      RDN other = (RDN) o;
    if ((o == null) || (! (o instanceof RDN)))
    {
      String nvalue1 = toNormalizedString();
      String nvalue2 = other.toNormalizedString();
      return nvalue1.equals(nvalue2);
    } else {
      return false;
    }
    RDN rdn = (RDN) o;
    if (numValues != rdn.numValues)
    {
      return false;
    }
    for (int i=0; i < numValues; i++)
    {
      if ((! attributeTypes[i].equals(rdn.attributeTypes[i])) ||
          (! attributeValues[i].equals(rdn.attributeValues[i])))
      {
        return false;
      }
    }
    return true;
  }
  /**
   * Retrieves the hash code for this RDN.  It will be calculated as
   * the sum of the hash codes of the types and values.
   * the hash code of the RDN's normalized string representation.
   *
   * @return  The hash code for this RDN.
   */
  public int hashCode()
  {
  public int hashCode() {
    assert debugEnter(CLASS_NAME, "hashCode");
    int hashCode = 0;
    for (int i=0; i < numValues; i++)
    {
      hashCode += attributeTypes[i].hashCode() +
                  attributeValues[i].hashCode();
    }
    return hashCode;
    return toNormalizedString().hashCode();
  }
@@ -1077,28 +810,12 @@
   *
   * @return  A string representation of this RDN.
   */
  public String toString()
  {
    if (rdnString == null)
    {
  public String toString() {
    assert debugEnter(CLASS_NAME, "toString");
      StringBuilder buffer = new StringBuilder();
      buffer.append(attributeNames[0]);
      buffer.append("=");
      buffer.append(attributeValues[0].getDNStringValue());
      for (int i=1; i < numValues; i++)
      {
        buffer.append("+");
        buffer.append(attributeNames[i]);
        buffer.append("=");
        buffer.append(attributeValues[i].getDNStringValue());
      }
      rdnString = buffer.toString();
    }
    return rdnString;
    toString(buffer);
    return buffer.toString();
  }
@@ -1107,15 +824,29 @@
   * Appends a string representation of this RDN to the provided
   * buffer.
   *
   * @param  buffer  The buffer to which the string representation
   *                 should be appended.
   * @param buffer
   *          The buffer to which the string representation should be
   *          appended.
   */
  public void toString(StringBuilder buffer)
  {
  public void toString(StringBuilder buffer) {
    assert debugEnter(CLASS_NAME, "toString",
                      "java.lang.StringBuilder");
    buffer.append(toString());
    ensureNotNull(buffer);
    buffer.append(attributeNames[0]);
    buffer.append("=");
    String value = attributeValues[0].getStringValue();
    quoteAttributeValue(buffer, value);
    for (int i = 1; i < attributeTypes.length; i++) {
      buffer.append("+");
      buffer.append(attributeNames[i]);
      buffer.append("=");
      value = attributeValues[i].getStringValue();
      quoteAttributeValue(buffer, value);
    }
  }
@@ -1125,12 +856,36 @@
   *
   * @return  A normalized string representation of this RDN.
   */
  public String toNormalizedString()
  {
    if (normalizedRDN == null)
    {
      StringBuilder buffer = new StringBuilder();
      toNormalizedString(buffer);
  public String toNormalizedString() {
    if (normalizedRDN == null) {
      StringBuilder builder = new StringBuilder();
      if (attributeNames.length == 1) {
        // Optimize for the common case of a single AVA.
        appendNormalizedAVA(builder, attributeTypes[0],
            attributeValues[0]);
      } else {
        // Multiple AVAs require sorting.
        TreeMap<String, Integer> map;
        map = new TreeMap<String, Integer>();
        for (int i = 0; i < attributeTypes.length; i++) {
          map.put(attributeTypes[i].getNameOrOID(), i);
        }
        boolean isFirst = true;
        for (Integer i : map.values()) {
          if (!isFirst) {
            builder.append('+');
          } else {
            isFirst = false;
          }
          appendNormalizedAVA(builder, attributeTypes[i],
              attributeValues[i]);
        }
      }
      normalizedRDN = builder.toString();
    }
    return normalizedRDN;
@@ -1142,143 +897,899 @@
   * Appends a normalized string representation of this RDN to the
   * provided buffer.
   *
   * @param  buffer  The buffer to which to append the information.
   * @param buffer
   *          The buffer to which to append the information.
   */
  public void toNormalizedString(StringBuilder buffer)
  {
  public void toNormalizedString(StringBuilder buffer) {
    assert debugEnter(CLASS_NAME, "toNormalizedString",
                      "java.lang.StringBuilder");
    if (normalizedRDN != null)
    {
      buffer.append(normalizedRDN);
      return;
    ensureNotNull(buffer);
    buffer.append(toNormalizedString());
    }
    boolean bufferEmpty = (buffer.length() == 0);
    if (attributeNames.length == 1)
    {
      toLowerCase(attributeNames[0], buffer);
      buffer.append('=');
      try
      {
        buffer.append(
             attributeValues[0].getNormalizedDNStringValue());
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "toNormalizedString", e);
  /**
   * Compares this RDN with the provided RDN.
   * <p>
   * The comparison will be done in order of the sorted RDN
   * components. It will attempt to use an ordering matching rule for
   * the associated attributes (if one is provided), but will fall
   * back on a bytewise comparison of the normalized values if
   * necessary.
   *
   * @param rdn
   *          The RDN against which to compare this RDN.
   * @return A negative integer if this RDN should come before the
   *         provided RDN, a positive integer if this RDN should come
   *         after the provided RDN, or zero if there is no difference
   *         with regard to ordering.
   */
  public int compareTo(RDN rdn) {
    assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(rdn));
        buffer.append(attributeValues[0].getStringValue());
      }
    }
    else
    {
      TreeSet<String> rdnElementStrings = new TreeSet<String>();
    ensureNotNull(rdn);
      for (int i=0; i < attributeNames.length; i++)
      {
        StringBuilder b2 = new StringBuilder();
        toLowerCase(attributeNames[i], b2);
        b2.append('=');
    // Handle the common case efficiently.
    if (attributeTypes.length == 1
        && rdn.attributeTypes.length == 1) {
      AttributeType type1 = attributeTypes[0];
      AttributeType type2 = rdn.attributeTypes[0];
        try
        {
          b2.append(attributeValues[i].getNormalizedStringValue());
        }
        catch (Exception e)
        {
          assert debugException(CLASS_NAME, "toNormalizedString", e);
      AttributeValue value1 = attributeValues[0];
      AttributeValue value2 = rdn.attributeValues[0];
          b2.append(attributeValues[i].getStringValue());
      return compareAVA(type1, value1, type2, value2);
        }
        rdnElementStrings.add(b2.toString());
    // We have at least one multi-valued RDNs, so we need to sort.
    TreeMap<String, Integer> map1;
    TreeMap<String, Integer> map2;
    map1 = new TreeMap<String, Integer>();
    map2 = new TreeMap<String, Integer>();
    for (int i = 0; i < attributeTypes.length; i++) {
      map1.put(attributeTypes[i].getNameOrOID(), i);
      }
      Iterator<String> iterator = rdnElementStrings.iterator();
      buffer.append(iterator.next());
    for (int i = 0; i < rdn.attributeTypes.length; i++) {
      map2.put(rdn.attributeTypes[i].getNameOrOID(), i);
    }
      while (iterator.hasNext())
      {
        buffer.append('+');
        buffer.append(iterator.next());
    // Now compare the sorted AVAs.
    Iterator<Integer> i1= map1.values().iterator();
    Iterator<Integer> i2 = map2.values().iterator();
    while (i1.hasNext() && i2.hasNext()) {
      int int1 = i1.next();
      int int2 = i2.next();
      AttributeType type1 = attributeTypes[int1];
      AttributeType type2 = rdn.attributeTypes[int2];
      AttributeValue value1 = attributeValues[int1];
      AttributeValue value2 = rdn.attributeValues[int2];
      int rc = compareAVA(type1, value1, type2, value2);
      if (rc != 0) {
        return rc;
      }
    }
    if (bufferEmpty)
    {
      normalizedRDN = buffer.toString();
    // At least one of the iterators has finished.
    if (i1.hasNext() == false && i2.hasNext() == false) {
      return 0;
    } else if (i1.hasNext() == false) {
      return -1;
    } else {
      return 1;
    }
  }
  /**
   * Compares this RDN with the provided RDN based on an alphabetic
   * comparison of the attribute names and values.
   * Compare two AVAs for order.
   *
   * @param  rdn  The RDN against which to compare this RDN.
   *
   * @return  A negative integer if this RDN should come before the
   *          provided RDN, a positive integer if this RDN should come
   *          after the provided RDN, or zero if there is no
   *          difference with regard to ordering.
   * @param type1
   *          The attribute type of the first AVA.
   * @param value1
   *          The attribute value of the first AVA.
   * @param type2
   *          The attribute type of the second AVA.
   * @param value2
   *          The attribute value of the second AVA.
   * @return Returns a negative integer, zero, or a positive integer
   *         if the first AVA is less than, equal to, or greater than
   *         the second.
   */
  public int compareTo(RDN rdn)
  {
    assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(rdn));
  private int compareAVA(AttributeType type1, AttributeValue value1,
      AttributeType type2, AttributeValue value2) {
    if (type1.equals(type2)) {
      OrderingMatchingRule rule = type1.getOrderingMatchingRule();
    if (equals(rdn))
    {
      return 0;
      try {
        if (rule != null) {
          byte[] b1 = value1.getNormalizedValueBytes();
          byte[] b2 = value2.getNormalizedValueBytes();
          return rule.compare(b1, b2);
        } else {
          byte[] b1 = value1.getNormalizedValue().value();
          byte[] b2 = value2.getNormalizedValue().value();
          return StaticUtils.compare(b1, b2);
    }
      } catch (Exception e) {
        assert debugException(CLASS_NAME, "compareAVA", e);
    int minValues = Math.min(numValues, rdn.numValues);
    for (int i=0; i < minValues; i++)
    {
      String n1 = attributeNames[i].toLowerCase();
      String n2 = rdn.attributeNames[i].toLowerCase();
        // Just get the raw values and do a comparison between them.
        byte[] b1 = value1.getValue().value();
        byte[] b2 = value2.getValue().value();
      int result = n1.compareTo(n2);
      if (result != 0)
      {
        return result;
        return StaticUtils.compare(b1, b2);
      }
    } else {
      String name1 = toLowerCase(type1.getNameOrOID());
      String name2 = toLowerCase(type2.getNameOrOID());
      try
      {
        String v1 = attributeValues[i].getNormalizedStringValue();
        String v2 = rdn.attributeValues[i].getNormalizedStringValue();
        result = v1.compareTo(v2);
        if (result != 0)
        {
          return result;
        }
      }
      catch (Exception e)
      {
        assert debugException(CLASS_NAME, "compareTo", e);
        return 0;
      return name1.compareTo(name2);
      }
    }
    if (numValues > minValues)
    {
      return 1;
  /**
   * Normalize and append the provided attribute type and value to the
   * provided buffer.
   *
   * @param buffer
   *          The string buffer.
   * @param type
   *          The attribute type.
   * @param value
   *          The attribute value.
   */
  private void appendNormalizedAVA(StringBuilder buffer,
      AttributeType type, AttributeValue value) {
    toLowerCase(type.getNameOrOID(), buffer);
    buffer.append('=');
    try {
      quoteAttributeValue(buffer, value.getNormalizedStringValue());
    } catch (Exception e) {
      assert debugException(CLASS_NAME, "toNormalizedString", e);
      quoteAttributeValue(buffer, value.getStringValue());
    }
    else if (rdn.numValues > minValues)
    {
      return -1;
    }
    else
    {
      return 0;
  /**
   * Encode an attribute value according to the DN string encoding
   * rules, and append it to the provided buffer.
   *
   * @param buffer
   *          Append the attribtue value to this buffer.
   * @param value
   *          The value to be represented in a DN-safe form.
   */
  private void quoteAttributeValue(StringBuilder buffer,
      String value) {
    assert debugEnter(CLASS_NAME, "quoteAttributeValue", String
        .valueOf(value));
    // Do nothing if the value is empty.
    int length = value.length();
    if (length == 0) {
      return;
    }
    // Assume 1-byte UTF8 and that no quoting will be required.
    buffer.ensureCapacity(buffer.length() + length);
    // Quote leading space or #.
    char c = value.charAt(0);
    if (c == ' ' || c == '#') {
      buffer.append('\\');
      buffer.append(c);
    } else {
      quoteChar(buffer, c);
    }
    // Process the remainder of the string.
    for (int i = 1; i < (length - 1); i++) {
      quoteChar(buffer, value.charAt(i));
    }
    // Quote trailing space.
    if (length > 1) {
      c = value.charAt(length - 1);
      if (c == ' ') {
        buffer.append('\\');
        buffer.append(c);
      } else {
        quoteChar(buffer, c);
    }
  }
}
  /**
   * Encode a single attribute value from an RDN according to the DN
   * string encoding rules.
   *
   * @param buffer
   *          Append the character to this buffer.
   * @param c
   *          The character to be encoded.
   */
  private void quoteChar(StringBuilder buffer, char c) {
    if ((c < ' ') || (c > '~')) {
      for (byte b : getBytes(String.valueOf(c))) {
        buffer.append('\\');
        buffer.append(byteToLowerHex(b));
      }
    } else {
      switch (c) {
      case ',':
      case '+':
      case '"':
      case '\\':
      case '<':
      case '>':
      case ';':
        buffer.append('\\');
      }
      buffer.append(c);
    }
  }
  /**
   * Parses an attribute name from the provided DN string starting at
   * the specified location.
   *
   * @param dnString
   *          The DN string to be parsed.
   * @param pos
   *          The position at which to start parsing the attribute
   *          name.
   * @param attributeName
   *          The buffer to which to append the parsed attribute name.
   * @return The position of the first character that is not part of
   *         the attribute name.
   * @throws DirectoryException
   *           If it was not possible to parse a valid attribute name
   *           from the provided DN string.
   */
  private static int parseAttributeName(String dnString, int pos,
      StringBuilder attributeName) throws DirectoryException {
    assert debugEnter(CLASS_NAME, "parseAttributeName", String
        .valueOf(dnString), String.valueOf(pos),
        "java.lang.StringBuilder");
    boolean allowExceptions = DirectoryServer
        .allowAttributeNameExceptions();
    int length = dnString.length();
    // Skip over any leading spaces.
    if (pos < length) {
      while (dnString.charAt(pos) == ' ') {
        pos++;
        if (pos == length) {
          // This means that the remainder of the DN was completely
          // comprised of spaces. If we have gotten here, then we
          // know that there is at least one RDN component, and
          // therefore the last non-space character of the DN must
          // have been a comma. This is not acceptable.
          int msgID = MSGID_ATTR_SYNTAX_DN_END_WITH_COMMA;
          String message = getMessage(msgID, dnString);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
      }
    }
    // Next, we should find the attribute name for this RDN component.
    // It may either be a name (with only letters, digits, and dashes
    // and starting with a letter) or an OID (with only digits and
    // periods, optionally prefixed with "oid."), and there is also a
    // special case in which we will allow underscores. Because of
    // the complexity involved, read the entire name first with
    // minimal validation and then do more thorough validation later.
    boolean checkForOID = false;
    boolean endOfName = false;
    while (pos < length) {
      // To make the switch more efficient, we'll include all ASCII
      // characters in the range of allowed values and then reject the
      // ones that aren't allowed.
      char c = dnString.charAt(pos);
      switch (c) {
      case ' ':
        // This should denote the end of the attribute name.
        endOfName = true;
        break;
      case '!':
      case '"':
      case '#':
      case '$':
      case '%':
      case '&':
      case '\'':
      case '(':
      case ')':
      case '*':
      case '+':
      case ',':
        // None of these are allowed in an attribute name or any
        // character immediately following it.
        int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        String message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case '-':
        // This will be allowed as long as it isn't the first
        // character in the attribute name.
        if (attributeName.length() > 0) {
          attributeName.append(c);
        } else {
          msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DASH;
          message = getMessage(msgID, dnString, c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
        break;
      case '.':
        // The period could be allowed if the attribute name is
        // actually expressed as an OID. We'll accept it for now,
        // but make sure to check it later.
        attributeName.append(c);
        checkForOID = true;
        break;
      case '/':
        // This is not allowed in an attribute name or any character
        // immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case '0':
      case '1':
      case '2':
      case '3':
      case '4':
      case '5':
      case '6':
      case '7':
      case '8':
      case '9':
        // Digits are always allowed if they are not the first
        // character. However, they may be allowed if they are the
        // first character if the valid is an OID or if the
        // attribute name exceptions option is enabled. Therefore,
        // we'll accept it now and check it later.
        attributeName.append(c);
        break;
      case ':':
      case ';': // NOTE: attribute options are not allowed in a DN.
      case '<':
        // None of these are allowed in an attribute name or any
        // character immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case '=':
        // This should denote the end of the attribute name.
        endOfName = true;
        break;
      case '>':
      case '?':
      case '@':
        // None of these are allowed in an attribute name or any
        // character immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case 'A':
      case 'B':
      case 'C':
      case 'D':
      case 'E':
      case 'F':
      case 'G':
      case 'H':
      case 'I':
      case 'J':
      case 'K':
      case 'L':
      case 'M':
      case 'N':
      case 'O':
      case 'P':
      case 'Q':
      case 'R':
      case 'S':
      case 'T':
      case 'U':
      case 'V':
      case 'W':
      case 'X':
      case 'Y':
      case 'Z':
        // These will always be allowed.
        attributeName.append(c);
        break;
      case '[':
      case '\\':
      case ']':
      case '^':
        // None of these are allowed in an attribute name or any
        // character immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case '_':
        // This will never be allowed as the first character. It
        // may be allowed for subsequent characters if the attribute
        // name exceptions option is enabled.
        if (attributeName.length() == 0) {
          msgID =
            MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_UNDERSCORE;
          message = getMessage(msgID, dnString,
              ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        } else if (allowExceptions) {
          attributeName.append(c);
        } else {
          msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_UNDERSCORE_CHAR;
          message = getMessage(msgID, dnString,
              ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
        break;
      case '`':
        // This is not allowed in an attribute name or any character
        // immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      case 'a':
      case 'b':
      case 'c':
      case 'd':
      case 'e':
      case 'f':
      case 'g':
      case 'h':
      case 'i':
      case 'j':
      case 'k':
      case 'l':
      case 'm':
      case 'n':
      case 'o':
      case 'p':
      case 'q':
      case 'r':
      case 's':
      case 't':
      case 'u':
      case 'v':
      case 'w':
      case 'x':
      case 'y':
      case 'z':
        // These will always be allowed.
        attributeName.append(c);
        break;
      default:
        // This is not allowed in an attribute name or any character
        // immediately following it.
        msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_CHAR;
        message = getMessage(msgID, dnString, c, pos);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
      if (endOfName) {
        break;
      }
      pos++;
    }
    // We should now have the full attribute name. However, we may
    // still need to perform some validation, particularly if the
    // name contains a period or starts with a digit. It must also
    // have at least one character.
    if (attributeName.length() == 0) {
      int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_NO_NAME;
      String message = getMessage(msgID, dnString);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
          message, msgID);
    } else if (checkForOID) {
      boolean validOID = true;
      int namePos = 0;
      int nameLength = attributeName.length();
      char ch = attributeName.charAt(0);
      if ((ch == 'o') || (ch == 'O')) {
        if (nameLength <= 4) {
          validOID = false;
        } else {
          if ((((ch = attributeName.charAt(1)) == 'i') || (ch == 'I'))
              && (((ch = attributeName.charAt(2)) == 'd')
                  || (ch == 'D'))
              && (attributeName.charAt(3) == '.')) {
            attributeName.delete(0, 4);
            nameLength -= 4;
          } else {
            validOID = false;
          }
        }
      }
      while (validOID && (namePos < nameLength)) {
        ch = attributeName.charAt(namePos++);
        if (isDigit(ch)) {
          while (validOID && (namePos < nameLength)
              && isDigit(attributeName.charAt(namePos))) {
            namePos++;
          }
          if ((namePos < nameLength)
              && (attributeName.charAt(namePos) != '.')) {
            validOID = false;
          }
        } else if (ch == '.') {
          if ((namePos == 1)
              || (attributeName.charAt(namePos - 2) == '.')) {
            validOID = false;
          }
        } else {
          validOID = false;
        }
      }
      if (validOID && (attributeName.charAt(nameLength - 1) == '.')) {
        validOID = false;
      }
      if (!validOID) {
        int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_PERIOD;
        String message = getMessage(msgID, dnString, attributeName
            .toString());
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
    } else if (isDigit(attributeName.charAt(0))
        && (!allowExceptions)) {
      int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_ILLEGAL_INITIAL_DIGIT;
      String message = getMessage(msgID, dnString, attributeName
          .charAt(0), ATTR_ALLOW_ATTRIBUTE_NAME_EXCEPTIONS);
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
          message, msgID);
    }
    return pos;
  }
  /**
   * Parses the attribute value from the provided DN string starting
   * at the specified location. When the value has been parsed, it
   * will be assigned to the provided ASN.1 octet string.
   *
   * @param dnString
   *          The DN string to be parsed.
   * @param pos
   *          The position of the first character in the attribute
   *          value to parse.
   * @param attributeValue
   *          The ASN.1 octet string whose value should be set to the
   *          parsed attribute value when this method completes
   *          successfully.
   * @return The position of the first character that is not part of
   *         the attribute value.
   * @throws DirectoryException
   *           If it was not possible to parse a valid attribute value
   *           from the provided DN string.
   */
  private static int parseAttributeValue(String dnString, int pos,
      ByteString attributeValue) throws DirectoryException {
    assert debugEnter(CLASS_NAME, "parseAttributeValue", String
        .valueOf(dnString), String.valueOf(pos),
        "java.lang.StringBuilder");
    // All leading spaces have already been stripped so we can start
    // reading the value. However, it may be empty so check for that.
    int length = dnString.length();
    if (pos >= length) {
      attributeValue.setValue("");
      return pos;
    }
    // Look at the first character. If it is an octothorpe (#), then
    // that means that the value should be a hex string.
    char c = dnString.charAt(pos++);
    if (c == '#') {
      // The first two characters must be hex characters.
      StringBuilder hexString = new StringBuilder();
      if ((pos + 2) > length) {
        int msgID = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
        String message = getMessage(msgID, dnString);
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
      for (int i = 0; i < 2; i++) {
        c = dnString.charAt(pos++);
        if (isHexDigit(c)) {
          hexString.append(c);
        } else {
          int msgID = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, dnString, c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
      }
      // The rest of the value must be a multiple of two hex
      // characters. The end of the value may be designated by the
      // end of the DN, a comma or semicolon, or a space.
      while (pos < length) {
        c = dnString.charAt(pos++);
        if (isHexDigit(c)) {
          hexString.append(c);
          if (pos < length) {
            c = dnString.charAt(pos++);
            if (isHexDigit(c)) {
              hexString.append(c);
            } else {
              int msgID = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
              String message = getMessage(msgID, dnString, c);
              throw new DirectoryException(
                  ResultCode.INVALID_DN_SYNTAX, message, msgID);
            }
          } else {
            int msgID = MSGID_ATTR_SYNTAX_DN_HEX_VALUE_TOO_SHORT;
            String message = getMessage(msgID, dnString);
            throw new DirectoryException(
                ResultCode.INVALID_DN_SYNTAX, message, msgID);
          }
        } else if ((c == ' ') || (c == ',') || (c == ';')
            || (c == '+')) {
          // This denotes the end of the value.
          pos--;
          break;
        } else {
          int msgID = MSGID_ATTR_SYNTAX_DN_INVALID_HEX_DIGIT;
          String message = getMessage(msgID, dnString, c);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
      }
      // At this point, we should have a valid hex string. Convert it
      // to a byte array and set that as the value of the provided
      // octet string.
      try {
        attributeValue.setValue(hexStringToByteArray(hexString
            .toString()));
        return pos;
      } catch (Exception e) {
        assert debugException(CLASS_NAME, "parseAttributeValue", e);
        int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_VALUE_DECODE_FAILURE;
        String message = getMessage(msgID, dnString, String
            .valueOf(e));
        throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
            message, msgID);
      }
    }
    // If the first character is a quotation mark, then the value
    // should continue until the corresponding closing quotation mark.
    else if (c == '"') {
      // Keep reading until we find an unescaped closing quotation
      // mark.
      boolean escaped = false;
      StringBuilder valueString = new StringBuilder();
      while (true) {
        if (pos >= length) {
          // We hit the end of the DN before the closing quote.
          // That's an error.
          int msgID = MSGID_ATTR_SYNTAX_DN_UNMATCHED_QUOTE;
          String message = getMessage(msgID, dnString);
          throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
              message, msgID);
        }
        c = dnString.charAt(pos++);
        if (escaped) {
          // The previous character was an escape, so we'll take this
          // one no matter what.
          valueString.append(c);
          escaped = false;
        } else if (c == '\\') {
          // The next character is escaped. Set a flag to denote
          // this, but don't include the backslash.
          escaped = true;
        } else if (c == '"') {
          // This is the end of the value.
          break;
        } else {
          // This is just a regular character that should be in the
          // value.
          valueString.append(c);
        }
      }
      attributeValue.setValue(valueString.toString());
      return pos;
    }
    // Otherwise, use general parsing to find the end of the value.
    else {
      boolean escaped;
      StringBuilder valueString = new StringBuilder();
      StringBuilder hexChars = new StringBuilder();
      if (c == '\\') {
        escaped = true;
      } else {
        escaped = false;
        valueString.append(c);
      }
      // Keep reading until we find an unescaped comma or plus sign or
      // the end of the DN.
      while (true) {
        if (pos >= length) {
          // This is the end of the DN and therefore the end of the
          // value. If there are any hex characters, then we need to
          // deal with them accordingly.
          appendHexChars(dnString, valueString, hexChars);
          break;
        }
        c = dnString.charAt(pos++);
        if (escaped) {
          // The previous character was an escape, so we'll take this
          // one. However, this could be a hex digit, and if that's
          // the case then the escape would actually be in front of
          // two hex digits that should be treated as a special
          // character.
          if (isHexDigit(c)) {
            // It is a hexadecimal digit, so the next digit must be
            // one too. However, this could be just one in a series
            // of escaped hex pairs that is used in a string
            // containing one or more multi-byte UTF-8 characters so
            // we can't just treat this byte in isolation. Collect
            // all the bytes together and make sure to take care of
            // these hex bytes before appending anything else to the
            // value.
            if (pos >= length) {
              int msgID =
                MSGID_ATTR_SYNTAX_DN_ESCAPED_HEX_VALUE_INVALID;
              String message = getMessage(msgID, dnString);
              throw new DirectoryException(
                  ResultCode.INVALID_DN_SYNTAX, message, msgID);
            } else {
              char c2 = dnString.charAt(pos++);
              if (isHexDigit(c2)) {
                hexChars.append(c);
                hexChars.append(c2);
              } else {
                int msgID =
                  MSGID_ATTR_SYNTAX_DN_ESCAPED_HEX_VALUE_INVALID;
                String message = getMessage(msgID, dnString);
                throw new DirectoryException(
                    ResultCode.INVALID_DN_SYNTAX, message, msgID);
              }
            }
          } else {
            appendHexChars(dnString, valueString, hexChars);
            valueString.append(c);
          }
          escaped = false;
        } else if (c == '\\') {
          escaped = true;
        } else if ((c == ',') || (c == ';') || (c == '+')) {
          appendHexChars(dnString, valueString, hexChars);
          pos--;
          break;
        } else {
          appendHexChars(dnString, valueString, hexChars);
          valueString.append(c);
        }
      }
      // Strip off any unescaped spaces that may be at the end of the
      // value.
      if (pos > 2 && dnString.charAt(pos - 1) == ' '
          && dnString.charAt(pos - 2) != '\\') {
        int lastPos = valueString.length() - 1;
        while (lastPos > 0) {
          if (valueString.charAt(lastPos) == ' ') {
            valueString.delete(lastPos, lastPos + 1);
            lastPos--;
          } else {
            break;
          }
        }
      }
      attributeValue.setValue(valueString.toString());
      return pos;
    }
  }
  /**
   * Decodes a hexadecimal string from the provided
   * <code>hexChars</code> buffer, converts it to a byte array, and
   * then converts that to a UTF-8 string. The resulting UTF-8 string
   * will be appended to the provided <code>valueString</code>
   * buffer, and the <code>hexChars</code> buffer will be cleared.
   *
   * @param dnString
   *          The DN string that is being decoded.
   * @param valueString
   *          The buffer containing the value to which the decoded
   *          string should be appended.
   * @param hexChars
   *          The buffer containing the hexadecimal characters to
   *          decode to a UTF-8 string.
   * @throws DirectoryException
   *           If any problem occurs during the decoding process.
   */
  private static void appendHexChars(String dnString,
      StringBuilder valueString, StringBuilder hexChars)
      throws DirectoryException {
    try {
      byte[] hexBytes = hexStringToByteArray(hexChars.toString());
      valueString.append(new String(hexBytes, "UTF-8"));
      hexChars.delete(0, hexChars.length());
    } catch (Exception e) {
      assert debugException(CLASS_NAME, "appendHexChars", e);
      int msgID = MSGID_ATTR_SYNTAX_DN_ATTR_VALUE_DECODE_FAILURE;
      String message = getMessage(msgID, dnString, String.valueOf(e));
      throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
          message, msgID);
    }
  }
}
opends/src/server/org/opends/server/types/RDNComparator.java
File was deleted
opends/src/server/org/opends/server/types/SearchFilter.java
@@ -3558,21 +3558,23 @@
    // attributes, then do so.
    if (dnAttributes)
    {
      for (RDN rdn : entry.getDN().getRDNComponents())
      DN entryDN = entry.getDN();
      int count = entryDN.getNumComponents();
      for (int rdnIndex = 0; rdnIndex < count; rdnIndex++)
      {
        AttributeType[]  types  = rdn.getAttributeTypes();
        AttributeValue[] values = rdn.getAttributeValues();
        for (int i=0; i < types.length; i++)
        RDN rdn = entryDN.getRDN(rdnIndex);
        int numAVAs = rdn.getNumValues();
        for (int i=0; i < numAVAs; i++)
        {
          try
          {
            if ((attributeType == null) ||
                attributeType.equals(types[i]))
                attributeType.equals(rdn.getAttributeType(i)))
            {
              AttributeValue v = rdn.getAttributeValue(i);
              ByteString nv =
                   matchingRule.normalizeValue(values[i].getValue());
                   matchingRule.normalizeValue(v.getValue());
              ConditionResult r =
                   matchingRule.valuesMatch(nv, normalizedValue);
              switch (r)
opends/src/server/org/opends/server/util/LDIFReader.java
@@ -580,7 +580,7 @@
    int length = line.length();
    if (colonPos == (length-1))
    {
      return new DN(new ArrayList<RDN>(0));
      return DN.nullDN();
    }
    if (line.charAt(colonPos+1) == ':')
opends/src/server/org/opends/server/util/StaticUtils.java
@@ -1273,13 +1273,57 @@
  /**
   * Indicates whether the two array lists are equal.  They will be considered
   * equal if they have the same number of elements, and the corresponding
   * elements between them are equal (in the same order).
   * Compare two byte arrays for order. Returns a negative integer,
   * zero, or a positive integer as the first argument is less than,
   * equal to, or greater than the second.
   *
   * @param  list1  The first list for which to make the determination.
   * @param  list2  The second list for which to make the determination.
   * @param a
   *          The first byte array to be compared.
   * @param a2
   *          The second byte array to be compared.
   * @return Returns a negative integer, zero, or a positive integer
   *         if the first byte array is less than, equal to, or greater
   *         than the second.
   */
  public static int compare(byte[] a, byte[] a2) {
    if (a == a2) {
      return 0;
    }
    if (a == null) {
      return -1;
    }
    if (a2 == null) {
      return 1;
    }
    int minLength = Math.min(a.length, a2.length);
    for (int i = 0; i < minLength; i++) {
      if (a[i] != a2[i]) {
        if (a[i] < a2[i]) {
          return -1;
        } else if (a[i] > a2[i]) {
          return 1;
        }
      }
    }
    return (a.length - a2.length);
  }
  /**
   * Indicates whether the two array lists are equal. They will be
   * considered equal if they have the same number of elements, and
   * the corresponding elements between them are equal (in the same
   * order).
   *
   * @param list1
   *          The first list for which to make the determination.
   * @param list2
   *          The second list for which to make the determination.
   * @return  <CODE>true</CODE> if the two array lists are equal, or
   *          <CODE>false</CODE> if they are not.
   */
@@ -3259,29 +3303,28 @@
    // Get the information about the RDN attributes.
    RDN rdn = dn.getRDN();
    AttributeType[]  rdnTypes  = rdn.getAttributeTypes();
    String[]         rdnNames  = rdn.getAttributeNames();
    AttributeValue[] rdnValues = rdn.getAttributeValues();
    int numAVAs = rdn.getNumValues();
    // If there is only one RDN attribute, then see which objectclass we should
    // use.
    ObjectClass structuralClass;
    if (rdnTypes.length == 1)
    if (numAVAs == 1)
    {
      if (rdnTypes[0].hasName(ATTR_C))
      AttributeType attrType = rdn.getAttributeType(0);
      if (attrType.hasName(ATTR_C))
      {
        structuralClass = DirectoryServer.getObjectClass(OC_COUNTRY, true);
      }
      else if (rdnTypes[0].hasName(ATTR_DC))
      else if (attrType.hasName(ATTR_DC))
      {
        structuralClass = DirectoryServer.getObjectClass(OC_DOMAIN, true);
      }
      else if (rdnTypes[0].hasName(ATTR_O))
      else if (attrType.hasName(ATTR_O))
      {
        structuralClass = DirectoryServer.getObjectClass(OC_ORGANIZATION, true);
      }
      else if (rdnTypes[0].hasName(ATTR_OU))
      else if (attrType.hasName(ATTR_OU))
      {
        structuralClass =
             DirectoryServer.getObjectClass(OC_ORGANIZATIONAL_UNIT_LC, true);
@@ -3315,11 +3358,15 @@
         new LinkedHashMap<AttributeType,List<Attribute>>();
    boolean extensibleObjectAdded = false;
    for (int i=0; i < rdnTypes.length; i++)
    for (int i=0; i < numAVAs; i++)
    {
      AttributeType attrType = rdn.getAttributeType(i);
      AttributeValue attrValue = rdn.getAttributeValue(i);
      String attrName = rdn.getAttributeName(i);
      // First, see if this type is allowed by the untypedObject class.  If not,
      // then we'll need to include the extensibleObject class.
      if ((! structuralClass.isRequiredOrOptional(rdnTypes[i])) &&
      if ((! structuralClass.isRequiredOrOptional(attrType)) &&
          (! extensibleObjectAdded))
      {
        ObjectClass extensibleObjectOC =
@@ -3337,36 +3384,36 @@
      // Create the attribute and add it to the appropriate map.
      LinkedHashSet<AttributeValue> valueSet =
           new LinkedHashSet<AttributeValue>(1);
      valueSet.add(rdnValues[i]);
      valueSet.add(attrValue);
      if (rdnTypes[i].isOperational())
      if (attrType.isOperational())
      {
        List<Attribute> attrList = operationalAttributes.get(rdnTypes[i]);
        List<Attribute> attrList = operationalAttributes.get(attrType);
        if ((attrList == null) || attrList.isEmpty())
        {
          attrList = new ArrayList<Attribute>(1);
          attrList.add(new Attribute(rdnTypes[i], rdnNames[i], valueSet));
          operationalAttributes.put(rdnTypes[i], attrList);
          attrList.add(new Attribute(attrType, attrName, valueSet));
          operationalAttributes.put(attrType, attrList);
        }
        else
        {
          Attribute attr = attrList.get(0);
          attr.getValues().add(rdnValues[i]);
          attr.getValues().add(attrValue);
        }
      }
      else
      {
        List<Attribute> attrList = userAttributes.get(rdnTypes[i]);
        List<Attribute> attrList = userAttributes.get(attrType);
        if ((attrList == null) || attrList.isEmpty())
        {
          attrList = new ArrayList<Attribute>(1);
          attrList.add(new Attribute(rdnTypes[i], rdnNames[i], valueSet));
          userAttributes.put(rdnTypes[i], attrList);
          attrList.add(new Attribute(attrType, attrName, valueSet));
          userAttributes.put(attrType, attrList);
        }
        else
        {
          Attribute attr = attrList.get(0);
          attr.getValues().add(rdnValues[i]);
          attr.getValues().add(attrValue);
        }
      }
    }
opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/JebTestCase.java
@@ -39,7 +39,6 @@
import org.opends.server.tools.makeldif.MakeLDIFInputStream;
import org.opends.server.tools.makeldif.TemplateFile;
import org.opends.server.types.DN;
import org.opends.server.types.DNComparator;
import org.opends.server.types.LDIFImportConfig;
import org.opends.server.types.ResultCode;
import org.opends.server.DirectoryServerTestCase;
@@ -53,8 +52,7 @@
 */
@Test(groups = { "precommit", "jeb" })
public abstract class JebTestCase extends DirectoryServerTestCase {
    private DNComparator comparator = new DNComparator();
    private TreeMap<DN,Entry> entryTreeMap = new TreeMap<DN,Entry>(comparator);
    private TreeMap<DN,Entry> entryTreeMap = new TreeMap<DN,Entry>();
    int numEntries;
    
    /**
opends/tests/unit-tests-testng/src/server/org/opends/server/core/BindOperationTestCase.java
@@ -116,17 +116,17 @@
                        noControls, new ASN1OctetString("cn=Directory Manager"),
                        new ASN1OctetString("password")),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, new DN(), new ASN1OctetString()),
                        null, DN.nullDN(), new ASN1OctetString()),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, new DN(), new ASN1OctetString()),
                        noControls, DN.nullDN(), new ASN1OctetString()),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, nullDN, new ASN1OctetString()),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, nullDN, new ASN1OctetString()),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, new DN(), nullOS),
                        null, DN.nullDN(), nullOS),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, new DN(), nullOS),
                        noControls, DN.nullDN(), nullOS),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, nullDN, nullOS),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
@@ -187,18 +187,18 @@
                        noControls, nullOS, "PLAIN",
                        new ASN1OctetString("\u0000u:test.user\u0000password")),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, new DN(), "EXTERNAL", null),
                        null, DN.nullDN(), "EXTERNAL", null),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, new DN(), "EXTERNAL", null),
                        noControls, DN.nullDN(), "EXTERNAL", null),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, nullDN, "EXTERNAL", null),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, nullDN, "EXTERNAL", null),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, new DN(), "PLAIN",
                        null, DN.nullDN(), "PLAIN",
                        new ASN1OctetString("\u0000u:test.user\u0000password")),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        noControls, new DN(), "PLAIN",
                        noControls, DN.nullDN(), "PLAIN",
                        new ASN1OctetString("\u0000u:test.user\u0000password")),
      new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                        null, nullDN, "PLAIN",
@@ -608,7 +608,7 @@
         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
    BindOperation bindOperation =
                       conn.processSASLBind(new DN(), "PLAIN", saslCreds);
                       conn.processSASLBind(DN.nullDN(), "PLAIN", saslCreds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(bindOperation.getSASLAuthUserEntry());
  }
@@ -676,7 +676,7 @@
         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
         conn.processSASLBind(DN.nullDN(), "PLAIN", saslCreds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(bindOperation.getUserEntryDN());
  }
@@ -721,7 +721,7 @@
         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
         conn.processSASLBind(DN.nullDN(), "PLAIN", saslCreds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    assertTrue(bindOperation.getProcessingStartTime() > 0);
    assertTrue(bindOperation.getProcessingStopTime() >=
@@ -793,7 +793,7 @@
         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
         conn.processSASLBind(DN.nullDN(), "PLAIN", saslCreds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    assertNotNull(bindOperation.getResponseLogElements());
    assertTrue(bindOperation.getResponseLogElements().length > 0);
@@ -867,7 +867,7 @@
         new ASN1OctetString("\u0000dn:cn=Directory Manager\u0000password");
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), "PLAIN", saslCreds);
         conn.processSASLBind(DN.nullDN(), "PLAIN", saslCreds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
    assertTrue(InvocationCounterPlugin.getPreParseCount() > 0);
@@ -1636,7 +1636,7 @@
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           requestControls, new DN(), new ASN1OctetString());
                           requestControls, DN.nullDN(), new ASN1OctetString());
    bindOperation.run();
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
@@ -1662,7 +1662,7 @@
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           requestControls, new DN(), "PLAIN", saslCreds);
                           requestControls, DN.nullDN(), "PLAIN", saslCreds);
    bindOperation.run();
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.UNAVAILABLE_CRITICAL_EXTENSION);
@@ -1685,7 +1685,7 @@
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           requestControls, new DN(), new ASN1OctetString());
                           requestControls, DN.nullDN(), new ASN1OctetString());
    bindOperation.run();
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
@@ -1710,7 +1710,7 @@
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           requestControls, new DN(), "PLAIN", saslCreds);
                           requestControls, DN.nullDN(), "PLAIN", saslCreds);
    bindOperation.run();
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/core/DeleteOperationTestCase.java
@@ -93,9 +93,9 @@
      new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                          null, new ASN1OctetString("o=test")),
      new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                          new ArrayList<Control>(), new DN()),
                          new ArrayList<Control>(), DN.nullDN()),
      new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                          null, new DN()),
                          null, DN.nullDN()),
      new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                          new ArrayList<Control>(), DN.decode("o=test")),
      new DeleteOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
opends/tests/unit-tests-testng/src/server/org/opends/server/core/ModifyOperationTestCase.java
@@ -196,9 +196,9 @@
                              new Attribute("description", "foo")));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null, new DN(), mods));
                                   conn.nextMessageID(), null, DN.nullDN(), mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), noControls, new DN(),
                                   conn.nextMessageID(), noControls, DN.nullDN(),
                                   mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null,
@@ -212,9 +212,9 @@
                              new Attribute("description", "foo")));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null, new DN(), mods));
                                   conn.nextMessageID(), null, DN.nullDN(), mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), noControls, new DN(),
                                   conn.nextMessageID(), noControls, DN.nullDN(),
                                   mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null,
@@ -228,9 +228,9 @@
                              new Attribute("description", "foo")));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null, new DN(), mods));
                                   conn.nextMessageID(), null, DN.nullDN(), mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), noControls, new DN(),
                                   conn.nextMessageID(), noControls, DN.nullDN(),
                                   mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null,
@@ -246,9 +246,9 @@
                              new Attribute("description", "bar")));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null, new DN(), mods));
                                   conn.nextMessageID(), null, DN.nullDN(), mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), noControls, new DN(),
                                   conn.nextMessageID(), noControls, DN.nullDN(),
                                   mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null,
@@ -264,9 +264,9 @@
                              new Attribute("cn", "bar")));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null, new DN(), mods));
                                   conn.nextMessageID(), null, DN.nullDN(), mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), noControls, new DN(),
                                   conn.nextMessageID(), noControls, DN.nullDN(),
                                   mods));
    opList.add(new ModifyOperation(conn, conn.nextOperationID(),
                                   conn.nextMessageID(), null,
@@ -375,7 +375,7 @@
                              new Attribute("description", "foo")));
    ModifyOperation modifyOperation =
         new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                             null, new DN(), mods);
                             null, DN.nullDN(), mods);
    assertNotNull(modifyOperation.getEntryDN());
  }
@@ -400,7 +400,7 @@
                              new Attribute("description", "foo")));
    ModifyOperation modifyOperation =
         new ModifyOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                             null, new DN(), mods);
                             null, DN.nullDN(), mods);
    assertNotNull(modifyOperation.getEntryDN());
    modifyOperation.setRawEntryDN(new ASN1OctetString("ou=Users,o=test"));
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestModifyDNOperation.java
@@ -241,8 +241,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -264,8 +266,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -296,8 +300,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -319,8 +325,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -351,8 +359,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -374,8 +384,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -406,8 +418,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -429,8 +443,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -461,8 +477,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -484,8 +502,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -516,8 +536,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -539,8 +561,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -798,8 +822,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -821,8 +847,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -856,8 +884,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -879,8 +909,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -941,8 +973,10 @@
    Entry newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.test0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    RDN rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
@@ -964,8 +998,10 @@
    newEntry = DirectoryServer.getEntry(DN.decode(
        "uid=user.0,ou=People,dc=example,dc=com"));
    assertNotNull(newEntry);
    for(AttributeType attribute : newEntry.getDN().getRDN().getAttributeTypes())
    rdn = newEntry.getDN().getRDN();
    for (int i = 0; i < rdn.getNumValues(); i++)
    {
      AttributeType attribute = rdn.getAttributeType(i);
      assertTrue(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.0")));
      assertFalse(newEntry.hasValue(attribute, null, new AttributeValue(attribute, "user.test0")));
    }
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestRFC3672SubtreeSpecification.java
@@ -40,7 +40,7 @@
    SubtreeSpecificationTestCase {
  // Cached root DN.
  private DN rootDN = new DN();
  private DN rootDN = DN.nullDN();
  /**
   * Tests the {@link RFC3672SubtreeSpecification#valueOf(DN, String)}
opends/tests/unit-tests-testng/src/server/org/opends/server/core/TestRelativeSubtreeSpecification.java
@@ -44,7 +44,7 @@
    SubtreeSpecificationTestCase {
  // Cached root DN.
  private DN rootDN = new DN();
  private DN rootDN = DN.nullDN();
  /**
   * Tests the {@link RelativeSubtreeSpecification#valueOf(DN, String)}
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/AnonymousSASLMechanismHandlerTestCase.java
@@ -139,7 +139,7 @@
         InternalClientConnection.getRootConnection();
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           new ArrayList<Control>(), new DN(),
                           new ArrayList<Control>(), DN.nullDN(),
                           SASL_MECHANISM_ANONYMOUS, null);
    handler.processSASLBind(bindOperation);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
@@ -166,7 +166,7 @@
         InternalClientConnection.getRootConnection();
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           new ArrayList<Control>(), new DN(),
                           new ArrayList<Control>(), DN.nullDN(),
                           SASL_MECHANISM_ANONYMOUS, new ASN1OctetString());
    handler.processSASLBind(bindOperation);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
@@ -192,7 +192,7 @@
         InternalClientConnection.getRootConnection();
    BindOperation bindOperation =
         new BindOperation(conn, conn.nextOperationID(), conn.nextMessageID(),
                           new ArrayList<Control>(), new DN(),
                           new ArrayList<Control>(), DN.nullDN(),
                           SASL_MECHANISM_ANONYMOUS,
                           new ASN1OctetString("Internal Trace String"));
    handler.processSASLBind(bindOperation);
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/CRAMMD5SASLMechanismHandlerTestCase.java
@@ -688,7 +688,7 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5,
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5,
                              new ASN1OctetString("invalid"));
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
@@ -708,12 +708,12 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5, null);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, null);
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.SASL_BIND_IN_PROGRESS);
    bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5,
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5,
                              new ASN1OctetString("malformed"));
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
@@ -733,14 +733,14 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5, null);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, null);
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.SASL_BIND_IN_PROGRESS);
    ASN1OctetString creds =
         new ASN1OctetString("dn:cn=Directory Manager malformeddigest");
    bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5, creds);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, creds);
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
@@ -760,7 +760,7 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5, null);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, null);
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.SASL_BIND_IN_PROGRESS);
@@ -768,7 +768,7 @@
         new ASN1OctetString("dn:cn=Directory Manager " +
                          "malformedcredswiththerightlength");
    bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_CRAM_MD5, creds);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_CRAM_MD5, creds);
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/extensions/DigestMD5SASLMechanismHandlerTestCase.java
@@ -823,7 +823,7 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_DIGEST_MD5,
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_DIGEST_MD5,
                              new ASN1OctetString("invalid"));
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
@@ -843,12 +843,12 @@
    InternalClientConnection conn =
         new InternalClientConnection(new AuthenticationInfo());
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_DIGEST_MD5, null);
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_DIGEST_MD5, null);
    assertEquals(bindOperation.getResultCode(),
                 ResultCode.SASL_BIND_IN_PROGRESS);
    bindOperation =
         conn.processSASLBind(new DN(), SASL_MECHANISM_DIGEST_MD5,
         conn.processSASLBind(DN.nullDN(), SASL_MECHANISM_DIGEST_MD5,
                              new ASN1OctetString("malformed"));
    assertFalse(bindOperation.getResultCode() == ResultCode.SUCCESS);
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java
@@ -140,10 +140,10 @@
         throws Exception
  {
    AttributeType cnType = DirectoryServer.getAttributeType(ATTR_COMMON_NAME);
    RDN[] components = new RDN[2];
    components[0] = new RDN(cnType, new AttributeValue(cnType, monitorName));
    components[1] = new RDN(cnType, new AttributeValue(cnType, "monitor"));
    DN monitorDN = new DN(components);
    RDN rdn0 = RDN.create(cnType, new AttributeValue(cnType, monitorName));
    RDN rdn1 = RDN.create(cnType, new AttributeValue(cnType, "monitor"));
    DN monitorDN = DN.create(rdn0, rdn1);
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalClientConnectionTestCase.java
@@ -449,7 +449,7 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    BindOperation bindOperation =
         conn.processSASLBind(new DN(), "PLAIN", creds);
         conn.processSASLBind(DN.nullDN(), "PLAIN", creds);
    assertEquals(bindOperation.getResultCode(), ResultCode.SUCCESS);
  }
@@ -902,7 +902,7 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(new DN(), SearchScope.BASE_OBJECT,
         conn.processSearch(DN.nullDN(), SearchScope.BASE_OBJECT,
              SearchFilter.createFilterFromString("(objectClass=*)"));
    assertEquals(searchOperation.getResultCode(), ResultCode.SUCCESS);
    assertFalse(searchOperation.getSearchEntries().isEmpty());
@@ -924,7 +924,7 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(new DN(), SearchScope.BASE_OBJECT,
         conn.processSearch(DN.nullDN(), SearchScope.BASE_OBJECT,
              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
              SearchFilter.createFilterFromString("(objectClass=*)"),
              new LinkedHashSet<String>());
@@ -952,7 +952,7 @@
    InternalClientConnection conn =
         InternalClientConnection.getRootConnection();
    InternalSearchOperation searchOperation =
         conn.processSearch(new DN(), SearchScope.BASE_OBJECT,
         conn.processSearch(DN.nullDN(), SearchScope.BASE_OBJECT,
              DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0, false,
              SearchFilter.createFilterFromString("(objectClass=*)"),
              new LinkedHashSet<String>(), searchListener);
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/internal/InternalSearchOperationTestCase.java
@@ -133,7 +133,7 @@
         InternalClientConnection.getRootConnection();
    new InternalSearchOperation(conn, conn.nextOperationID(),
                                conn.nextMessageID(), new ArrayList<Control>(),
                                new DN(), SearchScope.BASE_OBJECT,
                                DN.nullDN(), SearchScope.BASE_OBJECT,
                                DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0,
                                false, searchFilter,
                                new LinkedHashSet<String>(), null);
@@ -158,7 +158,7 @@
         InternalClientConnection.getRootConnection();
    new InternalSearchOperation(conn, conn.nextOperationID(),
                                conn.nextMessageID(), new ArrayList<Control>(),
                                new DN(), SearchScope.BASE_OBJECT,
                                DN.nullDN(), SearchScope.BASE_OBJECT,
                                DereferencePolicy.NEVER_DEREF_ALIASES, 0, 0,
                                false, searchFilter,
                                new LinkedHashSet<String>(),
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java
@@ -82,11 +82,9 @@
    AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
    RDN rdn = new RDN(attribute, attributeValue);
    RDN rdn = RDN.create(attribute, attributeValue);
    RDN[] rdns = {rdn};
    dn = new DN(rdns);
    dn = DN.create(rdn);
  }
  /**
@@ -295,7 +293,7 @@
    //Test case for a full encode decode operation with an empty DN params.
    addEncoded = new AddResponseProtocolOp(resultCode, resultMsg, new DN(),
    addEncoded = new AddResponseProtocolOp(resultCode, resultMsg, DN.nullDN(),
                                           referralURLs);
    element = addEncoded.encode();
    addDecoded = (AddResponseProtocolOp)AddResponseProtocolOp.decode(element);
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java
@@ -84,11 +84,8 @@
    AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
    RDN rdn = new RDN(attribute, attributeValue);
    RDN[] rdns = {rdn};
    dn = new DN(rdns);
    RDN rdn = RDN.create(attribute, attributeValue);
    dn = DN.create(rdn);
  }
  /**
@@ -303,7 +300,7 @@
    //Test case for a full encode decode operation with an empty DN params.
    deleteEncoded = new CompareResponseProtocolOp(resultCode, resultMsg, new DN(),
    deleteEncoded = new CompareResponseProtocolOp(resultCode, resultMsg, DN.nullDN(),
                                                 referralURLs);
    element = deleteEncoded.encode();
    deleteDecoded = (CompareResponseProtocolOp)CompareResponseProtocolOp.decode(
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java
@@ -82,11 +82,8 @@
    AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
    RDN rdn = new RDN(attribute, attributeValue);
    RDN[] rdns = {rdn};
    dn = new DN(rdns);
    RDN rdn = RDN.create(attribute, attributeValue);
    dn = DN.create(rdn);
  }
  /**
@@ -301,7 +298,7 @@
    //Test case for a full encode decode operation with an empty DN params.
    deleteEncoded = new DeleteResponseProtocolOp(resultCode, resultMsg, new DN(),
    deleteEncoded = new DeleteResponseProtocolOp(resultCode, resultMsg, DN.nullDN(),
                                           referralURLs);
    element = deleteEncoded.encode();
    deleteDecoded = (DeleteResponseProtocolOp)DeleteResponseProtocolOp.decode(
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java
@@ -84,11 +84,8 @@
    AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
    RDN rdn = new RDN(attribute, attributeValue);
    RDN[] rdns = {rdn};
    dn = new DN(rdns);
    RDN rdn = RDN.create(attribute, attributeValue);
    dn = DN.create(rdn);
  }
  /**
@@ -303,7 +300,7 @@
    //Test case for a full encode decode operation with an empty DN params.
    deleteEncoded = new ModifyDNResponseProtocolOp(resultCode, resultMsg, new DN(),
    deleteEncoded = new ModifyDNResponseProtocolOp(resultCode, resultMsg, DN.nullDN(),
                                                  referralURLs);
    element = deleteEncoded.encode();
    deleteDecoded = (ModifyDNResponseProtocolOp)ModifyDNResponseProtocolOp.decode(
opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java
@@ -84,11 +84,8 @@
    AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
    RDN rdn = new RDN(attribute, attributeValue);
    RDN[] rdns = {rdn};
    dn = new DN(rdns);
    RDN rdn = RDN.create(attribute, attributeValue);
    dn = DN.create(rdn);
  }
  /**
@@ -303,7 +300,7 @@
    //Test case for a full encode decode operation with an empty DN params.
    modifyEncoded = new ModifyResponseProtocolOp(resultCode, resultMsg, new DN(),
    modifyEncoded = new ModifyResponseProtocolOp(resultCode, resultMsg, DN.nullDN(),
                                                 referralURLs);
    element = modifyEncoded.encode();
    modifyDecoded = (ModifyResponseProtocolOp)ModifyResponseProtocolOp.decode(
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
@@ -26,16 +26,18 @@
 */
package org.opends.server.types;
import static org.testng.Assert.*;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.AfterClass;
import java.util.Arrays;
/**
 * This class defines a set of tests for the org.opends.server.core.DN
@@ -50,45 +52,73 @@
  @DataProvider(name = "testDNs")
  public Object[][] createData() {
    return new Object[][] {
        { "", "" },
        { "   ", "" },
        { "dc=com", "dc=com" },
        { "DC=COM", "dc=com" },
        { "dc = com", "dc=com" },
        { " dc = com ", "dc=com" },
        { "dc=example,dc=com", "dc=example,dc=com" },
        { "dc=example, dc=com", "dc=example,dc=com" },
        { "dc=example ,dc=com", "dc=example,dc=com" },
        { "dc =example , dc  =   com", "dc=example,dc=com" },
        { "", "", "" },
        { "   ", "", "" },
        { "dc=com", "dc=com", "dc=com" },
        { "DC=COM", "dc=com", "DC=COM" },
        { "dc = com", "dc=com", "dc=com" },
        { " dc = com ", "dc=com", "dc=com" },
        { "dc=example,dc=com", "dc=example,dc=com",
            "dc=example,dc=com" },
        { "dc=example, dc=com", "dc=example,dc=com",
            "dc=example,dc=com" },
        { "dc=example ,dc=com", "dc=example,dc=com",
            "dc=example,dc=com" },
        { "dc =example , dc  =   com", "dc=example,dc=com",
            "dc=example,dc=com" },
        { "givenName=John+cn=Doe,ou=People,dc=example,dc=com",
            "cn=doe+givenname=john,ou=people,dc=example,dc=com" },
            "cn=doe+givenname=john,ou=people,dc=example,dc=com",
            "givenName=John+cn=Doe,ou=People,dc=example,dc=com" },
        { "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com",
            "givenname=john\\+cn=doe,ou=people,dc=example,dc=com" },
            "givenname=john\\+cn=doe,ou=people,dc=example,dc=com",
            "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com" },
        { "cn=Doe\\, John,ou=People,dc=example,dc=com",
            "cn=doe\\, john,ou=people,dc=example,dc=com" },
        { "UID=jsmith,DC=example,DC=net", "uid=jsmith,dc=example,dc=net" },
            "cn=doe\\, john,ou=people,dc=example,dc=com",
            "cn=Doe\\, John,ou=People,dc=example,dc=com" },
        { "UID=jsmith,DC=example,DC=net",
            "uid=jsmith,dc=example,dc=net",
            "UID=jsmith,DC=example,DC=net" },
        { "OU=Sales+CN=J. Smith,DC=example,DC=net",
            "cn=j. smith+ou=sales,dc=example,dc=net" },
            "cn=j. smith+ou=sales,dc=example,dc=net",
            "OU=Sales+CN=J. Smith,DC=example,DC=net" },
        { "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
            "cn=james \\\"jim\\\" smith\\, iii,dc=example,dc=net" },
            "cn=james \\\"jim\\\" smith\\, iii,dc=example,dc=net",
            "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net" },
        { "CN=John Smith\\2C III,DC=example,DC=net",
            "cn=john smith\\, iii,dc=example,dc=net",
            "CN=John Smith\\, III,DC=example,DC=net" },
        { "CN=\\23John Smith\\20,DC=example,DC=net",
            "cn=\\#john smith,dc=example,dc=net",
            "CN=\\#John Smith\\ ,DC=example,DC=net" },
        { "CN=Before\\0dAfter,DC=example,DC=net",
            "cn=before\\0dafter,dc=example,dc=net" },
        { "1.3.6.1.4.1.1466.0=#04024869", "1.3.6.1.4.1.1466.0=\\04\\02hi" },
        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\\c4\\8di\\c4\\87" },
            "cn=before\\0dafter,dc=example,dc=net",
            "CN=Before\\0dAfter,DC=example,DC=net" },
        { "1.3.6.1.4.1.1466.0=#04024869",
            "1.3.6.1.4.1.1466.0=\\04\\02hi",
            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
        { "1.1.1=", "1.1.1=", "1.1.1=" },
        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\\c4\\8di\\c4\\87",
            "CN=Lu\\c4\\8di\\c4\\87" },
        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius",
            "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=airius" },
        { "photo=\\ john \\ ,dc=com", "photo=\\ john \\ ,dc=com" },
        { "AB-global=", "ab-global=" },
            "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=airius",
            "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius" },
        { "photo=\\ john \\ ,dc=com", "photo=\\ john \\ ,dc=com",
            "photo=\\ john \\ ,dc=com" },
        { "AB-global=", "ab-global=", "AB-global=" },
        { "OU= Sales + CN = J. Smith ,DC=example,DC=net",
             "cn=j. smith+ou=sales,dc=example,dc=net" },
         { "cn=John+a=", "a=+cn=john" },
            "cn=j. smith+ou=sales,dc=example,dc=net",
            "OU=Sales+CN=J. Smith,DC=example,DC=net" },
        { "cn=John+a=", "a=+cn=john", "cn=John+a=" },
         { "OID.1.3.6.1.4.1.1466.0=#04024869",
              "1.3.6.1.4.1.1466.0=\\04\\02hi" },
            "1.3.6.1.4.1.1466.0=\\04\\02hi",
            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
         { "O=\"Sue, Grabbit and Runn\",C=US",
              "o=sue\\, grabbit and runn,c=us" },
    };
            "o=sue\\, grabbit and runn,c=us",
            "O=Sue\\, Grabbit and Runn,C=US" }, };
  }
  /**
   * Illegal DN test data provider.
   *
@@ -96,66 +126,15 @@
   */
  @DataProvider(name = "illegalDNs")
  public Object[][] createIllegalData() {
    return new Object[][] {
         { "manager" },
         { "manager " },
         { "cn+Jim" },
         { "cn=Jim+" },
         { "cn=Jim," },
         { "cn=Jim,  " },
         { "cn+uid=Jim" },
         { "-cn=Jim" },
         { "/tmp=a" },
         { "\\tmp=a" },
         { "cn;lang-en=Jim" },
         { "@cn=Jim" },
         { "_name_=Jim" },
         { "\u03c0=pi" },
         { "v1.0=buggy" },
         { "1.3.6.1.4.1.1466..0=#04024869" },
    };
    return new Object[][] { { "manager" }, { "manager " },
        { "cn+Jim" }, { "cn=Jim+" }, { "cn=Jim," }, { "cn=Jim,  " },
        { "cn+uid=Jim" }, { "-cn=Jim" }, { "/tmp=a" }, { "\\tmp=a" },
        { "cn;lang-en=Jim" }, { "@cn=Jim" }, { "_name_=Jim" },
        { "\u03c0=pi" }, { "v1.0=buggy" },
        { "1.3.6.1.4.1.1466..0=#04024869" }, };
  }
  /**
   * DN compare test data provider.
   *
   * @return The unsorted and sorted DN strings .
   */
  @DataProvider(name = "compareDNs")
  public Object[][] createSortData() {
    return new Object[][] {
         {
              // Not sorted.
              new String[]
                   {
                        "UID=jsmith,DC=example,DC=net",
                        "dc=com",
                        "",
                        "dc=example,dc=net",
                        "uid=jsmith,dc=example,dc=com",
                        "dc=example,dc=com",
                        "cn=jsmith,dc=example,dc=com",
                        "",
                        "dc =example , dc  =   com",
                        "uid=asmith,dc=example,dc=net",
                   },
              // Sorted.
              new String[]
                   {
                        "",
                        "",
                        "dc=com",
                        "dc =example , dc  =   com",
                        "dc=example,dc=com",
                        "cn=jsmith,dc=example,dc=com",
                        "uid=jsmith,dc=example,dc=com",
                        "dc=example,dc=net",
                        "uid=asmith,dc=example,dc=net",
                        "UID=jsmith,DC=example,DC=net",
                   },
         }
    };
  }
  /**
   * Set up the environment for performing the tests in this suite.
@@ -165,138 +144,799 @@
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available, so we'll start
    // the server.
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    AttributeType dummy = DirectoryServer.getDefaultAttributeType(
        "x-test-integer-type", DirectoryServer
            .getDefaultIntegerSyntax());
    DirectoryServer.getSchema().registerAttributeType(dummy, true);
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testCreateNPE() throws Exception {
    DN.create((RDN[]) null);
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateNullDN1() throws Exception {
    DN dn = DN.create(new RDN[0]);
    assertEquals(dn, DN.nullDN());
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateNullDN2() throws Exception {
    DN dn = DN.create();
    assertEquals(dn, DN.nullDN());
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateWithSingleRDN1() throws Exception {
    DN dn = DN.create(new RDN[] { RDN.decode("dc=com") });
    assertEquals(dn, DN.decode("dc=com"));
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateWithSingleRDN2() throws Exception {
    DN dn = DN.create(RDN.decode("dc=com"));
    assertEquals(dn, DN.decode("dc=com"));
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateWithMultipleRDNs1() throws Exception {
    DN dn = DN.create(new RDN[] { RDN.decode("dc=foo"),
        RDN.decode("dc=opends"), RDN.decode("dc=org") });
    assertEquals(dn, DN.decode("dc=foo,dc=opends,dc=org"));
  }
  /**
   * Tests the create method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testCreateWithMultipleRDNs2() throws Exception {
    DN dn = DN.create(RDN.decode("dc=foo"), RDN.decode("dc=opends"),
        RDN.decode("dc=org"));
    assertEquals(dn, DN.decode("dc=foo,dc=opends,dc=org"));
  }
  /**
   * Tests the <CODE>decode</CODE> method which takes a String
   * argument.
   *
   * @param rawDN
   *          The raw undecoded DN string.
   *          Raw DN string representation.
   * @param normDN
   *          The expected normalized value.
   *          Normalized DN string representation.
   * @param stringDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testDNs")
  public void testDecodeString(String rawDN, String normDN)
      throws Exception {
  public void testDecodeString(String rawDN, String normDN,
      String stringDN) throws Exception {
    DN dn = DN.decode(rawDN);
    assertEquals(normDN, dn.toNormalizedString());
    assertEquals(dn.toNormalizedString(), normDN);
  }
  /**
   * Tests the <CODE>decode</CODE> method which takes a String
   * argument.
   *
   * @param rawDN
   *          The raw undecoded DN string.
   *          Raw DN string representation.
   * @param normDN
   *          The expected normalized value.
   *          Normalized DN string representation.
   * @param stringDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testDNs")
  public void testDecodeOctetString(String rawDN, String normDN)
      throws Exception {
  public void testDecodeOctetString(String rawDN, String normDN,
      String stringDN) throws Exception {
    ASN1OctetString octetString = new ASN1OctetString(rawDN);
    DN dn = DN.decode(octetString);
    assertEquals(normDN, dn.toNormalizedString());
    assertEquals(dn.toNormalizedString(), normDN);
  }
  /**
   * Tests the <CODE>valueOf</CODE> method which takes a String
   * argument.
   *
   * @param rawDN
   *          Raw DN string representation.
   * @param normDN
   *          Normalized DN string representation.
   * @param stringDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testDNs")
  public void testValueOf(String rawDN, String normDN, String stringDN)
      throws Exception {
    DN dn = DN.valueOf(rawDN);
    assertEquals(dn.toNormalizedString(), normDN);
  }
  /**
   * Test that decoding an illegal DN as a String throws an exception.
   * @param dn The illegal DN to be tested.
   *
   * @param dn
   *          The illegal DN to be tested.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "illegalDNs",
        expectedExceptions = DirectoryException.class )
  public void testIllegalStringDNs(String dn)
       throws Exception
  {
    try
    {
  @Test(dataProvider = "illegalDNs", expectedExceptions = DirectoryException.class)
  public void testIllegalStringDNs(String dn) throws Exception {
    try {
      DN.decode(dn);
    }
    catch (DirectoryException e)
    {
    } catch (DirectoryException e) {
      throw e;
    }
    catch (Exception e)
    {
      System.out.println(
           "Illegal DN <" + dn + "> threw the wrong type of exception");
    } catch (Exception e) {
      System.out.println("Illegal DN <" + dn
          + "> threw the wrong type of exception");
      throw e;
    }
    throw new RuntimeException(
         "Illegal DN <" + dn + "> did not throw an exception");
    throw new RuntimeException("Illegal DN <" + dn
        + "> did not throw an exception");
  }
  /**
   * Test that decoding an illegal DN as an octet string throws an exception.
   * @param dn The illegal DN to be tested.
   * Test that decoding an illegal DN as an octet string throws an
   * exception.
   *
   * @param dn
   *          The illegal DN to be tested.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "illegalDNs",
        expectedExceptions = DirectoryException.class )
  public void testIllegalOctetStringDNs(String dn)
       throws Exception
  {
  @Test(dataProvider = "illegalDNs", expectedExceptions = DirectoryException.class)
  public void testIllegalOctetStringDNs(String dn) throws Exception {
    ASN1OctetString octetString = new ASN1OctetString(dn);
    try
    {
    try {
      DN.decode(octetString);
    }
    catch (DirectoryException e)
    {
    } catch (DirectoryException e) {
      throw e;
    }
    catch (Exception e)
    {
      System.out.println(
           "Illegal DN <" + dn + "> threw the wrong type of exception");
    } catch (Exception e) {
      System.out.println("Illegal DN <" + dn
          + "> threw the wrong type of exception");
      throw e;
    }
    throw new RuntimeException(
         "Illegal DN <" + dn + "> did not throw an exception");
    throw new RuntimeException("Illegal DN <" + dn
        + "> did not throw an exception");
  }
  /**
   * Test the <CODE>compareTo</CODE> method.
   * @param unsorted An array of string DNs in no particular order.
   * @param sorted An array of the same DNs in sort order.
   * Test that decoding an illegal DN as a String throws an exception.
   *
   * @param dn
   *          The illegal DN to be tested.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "compareDNs")
  public void testCompareTo(String[] unsorted, String[] sorted)
       throws Exception
  {
    DN[] expected = new DN[sorted.length];
    for (int i = 0; i < sorted.length; i++)
    {
      expected[i] = DN.decode(sorted[i]);
  @Test(dataProvider = "illegalDNs", expectedExceptions = DirectoryException.class)
  public void testIllegalValueOf(String dn) throws Exception {
    try {
      DN.valueOf(dn);
    } catch (DirectoryException e) {
      throw e;
    } catch (Exception e) {
      System.out.println("Illegal DN <" + dn
          + "> threw the wrong type of exception");
      throw e;
    }
    throw new RuntimeException("Illegal DN <" + dn
        + "> did not throw an exception");
    }
    DN[] actual = new DN[unsorted.length];
    for (int i = 0; i < unsorted.length; i++)
    {
      actual[i] = DN.decode(unsorted[i]);
  /**
   * Test the nullDN method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testNullDN() throws Exception {
    DN nullDN = DN.nullDN();
    assertTrue(nullDN.getNumComponents() == 0);
    assertEquals(nullDN.toNormalizedString(), "");
    }
    Arrays.sort(actual);
  /**
   * Test the isNullDN method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testIsNullDNWithNullDN() throws Exception {
    DN nullDN = DN.nullDN();
    assertTrue(nullDN.isNullDN());
  }
  /**
   * Test the isNullDN method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testIsNullDNWithNonNullDN() throws Exception {
    DN dn = DN.decode("dc=com");
    assertFalse(dn.isNullDN());
  }
  /**
   * DN test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "createNumComponentsTestData")
  public Object[][] createNumComponentsTestData() {
    return new Object[][] { { "", 0 }, { "dc=com", 1 },
        { "dc=opends,dc=com", 2 },
        { "dc=world,dc=opends,dc=com", 3 },
        { "dc=hello,dc=world,dc=opends,dc=com", 4 }, };
  }
  /**
   * Test the getNumComponents method.
   *
   * @param s
   *          The test DN string.
   * @param sz
   *          The expected number of RDNs.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createNumComponentsTestData")
  public void testNumComponents(String s, int sz) throws Exception {
    DN dn = DN.decode(s);
    assertEquals(dn.getNumComponents(), sz);
  }
  /**
   * DN test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "createParentAndRDNTestData")
  public Object[][] createParentAndRDNTestData() {
    return new Object[][] {
        { "", null, null },
        { "dc=com", null, "dc=com" },
        { "dc=opends,dc=com", "dc=com", "dc=opends" },
        { "dc=world,dc=opends,dc=com", "dc=opends,dc=com", "dc=world" },
        { "dc=hello,dc=world,dc=opends,dc=com",
            "dc=world,dc=opends,dc=com", "dc=hello" }, };
  }
  /**
   * Test the getParent method.
   *
   * @param s
   *          The test DN string.
   * @param p
   *          The expected parent.
   * @param r
   *          The expected rdn.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createParentAndRDNTestData")
  public void testGetParent(String s, String p, String r)
      throws Exception {
    DN dn = DN.decode(s);
    DN parent = (p != null ? DN.decode(p) : null);
    assertEquals(dn.getParent(), parent, "For DN " + s);
  }
  /**
   * Test the getParent method's interaction with other methods.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetParentInteraction() throws Exception {
    DN c = DN.decode("dc=foo,dc=bar,dc=opends,dc=org");
    DN e = DN.decode("dc=bar,dc=opends,dc=org");
    DN p = c.getParent();
    assertFalse(p.isNullDN());
    assertEquals(p.getNumComponents(), 3);
    assertEquals(p.compareTo(c), -1);
    assertEquals(c.compareTo(p), 1);
    assertTrue(p.isAncestorOf(c));
    assertFalse(c.isAncestorOf(p));
    assertTrue(c.isDescendantOf(p));
    assertFalse(p.isDescendantOf(c));
    assertEquals(p, e);
    assertEquals(p.hashCode(), e.hashCode());
    assertEquals(p.toNormalizedString(), e.toNormalizedString());
    assertEquals(p.toString(), e.toString());
    assertEquals(p.getRDN(), RDN.decode("dc=bar"));
    assertEquals(p.getRDN(0), RDN.decode("dc=bar"));
    assertEquals(p.getRDN(1), RDN.decode("dc=opends"));
    assertEquals(p.getRDN(2), RDN.decode("dc=org"));
    assertEquals(p.getParent(), DN.decode("dc=opends,dc=org"));
    assertEquals(p.getParent(), e.getParent());
    assertEquals(p.concat(RDN.decode("dc=foo")), DN
        .decode("dc=foo,dc=bar,dc=opends,dc=org"));
    assertEquals(p.concat(RDN.decode("dc=foo")), c);
    assertEquals(p.concat(DN.decode("dc=xxx,dc=foo")), DN
        .decode("dc=xxx,dc=foo,dc=bar,dc=opends,dc=org"));
    assertEquals(p.getLocalName(1), DN.decode("dc=bar,dc=opends"));
    assertEquals(p.getLocalName(0, 2), DN.decode("dc=opends,dc=org"));
  }
  /**
   * Test the getRDN method.
   *
   * @param s
   *          The test DN string.
   * @param p
   *          The expected parent.
   * @param r
   *          The expected rdn.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createParentAndRDNTestData")
  public void testGetRDN(String s, String p, String r)
      throws Exception {
    DN dn = DN.decode(s);
    RDN rdn = (r != null ? RDN.decode(r) : null);
    assertEquals(dn.getRDN(), rdn, "For DN " + s);
  }
  /**
   * DN test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "createRDNTestData")
  public Object[][] createRDNTestData() {
    return new Object[][] { { "dc=com", 0, "dc=com" },
        { "dc=opends,dc=com", 0, "dc=opends" },
        { "dc=opends,dc=com", 1, "dc=com" },
        { "dc=hello,dc=world,dc=opends,dc=com", 0, "dc=hello" },
        { "dc=hello,dc=world,dc=opends,dc=com", 1, "dc=world" },
        { "dc=hello,dc=world,dc=opends,dc=com", 2, "dc=opends" },
        { "dc=hello,dc=world,dc=opends,dc=com", 3, "dc=com" }, };
  }
  /**
   * Test the getRDN indexed method.
   *
   * @param s
   *          The test DN string.
   * @param i
   *          The RDN index.
   * @param r
   *          The expected rdn.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNTestData")
  public void testGetRDNIndexed(String s, int i, String r)
      throws Exception {
    DN dn = DN.decode(s);
    RDN rdn = RDN.decode(r);
    assertEquals(dn.getRDN(i), rdn, "For DN " + s);
  }
  /**
   * DN test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "createRDNIllegalTestData")
  public Object[][] createRDNIllegalTestData() {
    return new Object[][] { { "", 0 }, { "", -1 }, { "", 1 },
        { "dc=com", -1 }, { "dc=com", 1 },
        { "dc=opends,dc=com", -1 }, { "dc=opends,dc=com", 2 },
        { "dc=hello,dc=world,dc=opends,dc=com", -1 },
        { "dc=hello,dc=world,dc=opends,dc=com", 4 }, };
  }
  /**
   * Test the getRDN indexed method with illegal indexes.
   *
   * @param s
   *          The test DN string.
   * @param i
   *          The illegal RDN index.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNIllegalTestData", expectedExceptions = IndexOutOfBoundsException.class)
  public void testGetRDNIndexedException(String s, int i)
      throws Exception {
    DN dn = DN.decode(s);
    // Shoudld throw.
    dn.getRDN(i);
    fail("Excepted exception for RDN index " + i + " in DN " + s);
  }
  /**
   * Concat DN test data provider.
   *
   * @return The array of test data.
   */
  @DataProvider(name = "createConcatDNTestData")
  public Object[][] createConcatDNTestData() {
    return new Object[][] {
        { "", "", "" },
        { "", "dc=org", "dc=org" },
        { "", "dc=opends,dc=org", "dc=opends,dc=org" },
        { "dc=org", "", "dc=org" },
        { "dc=org", "dc=opends", "dc=opends,dc=org" },
        { "dc=org", "dc=foo,dc=opends", "dc=foo,dc=opends,dc=org" },
        { "dc=opends,dc=org", "", "dc=opends,dc=org" },
        { "dc=opends,dc=org", "dc=foo", "dc=foo,dc=opends,dc=org" },
        { "dc=opends,dc=org", "dc=bar,dc=foo",
            "dc=bar,dc=foo,dc=opends,dc=org" }, };
  }
  /**
   * Test the concat(DN) method.
   *
   * @param s
   *          The test DN string.
   * @param l
   *          The local name to be appended.
   * @param e
   *          The expected DN.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createConcatDNTestData")
  public void testConcatDN(String s, String l, String e)
      throws Exception {
    DN dn = DN.decode(s);
    DN localName = DN.decode(l);
    DN expected = DN.decode(e);
    assertEquals(dn.concat(localName), expected);
  }
  /**
   * Test the concat(DN) method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConcatDNException() throws Exception {
    DN dn = DN.decode("dc=org");
    dn.concat((DN) null);
  }
  /**
   * Test the concat(DN) method's interaction with other methods.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testConcatDNInteraction() throws Exception {
    DN p = DN.decode("dc=opends,dc=org");
    DN l = DN.decode("dc=foo,dc=bar");
    DN e = DN.decode("dc=foo,dc=bar,dc=opends,dc=org");
    DN c = p.concat(l);
    assertFalse(c.isNullDN());
    assertEquals(c.getNumComponents(), 4);
    assertEquals(c.compareTo(p), 1);
    assertEquals(p.compareTo(c), -1);
    assertTrue(p.isAncestorOf(c));
    assertFalse(c.isAncestorOf(p));
    assertTrue(c.isDescendantOf(p));
    assertFalse(p.isDescendantOf(c));
    assertEquals(c, e);
    assertEquals(c.hashCode(), e.hashCode());
    assertEquals(c.toNormalizedString(), e.toNormalizedString());
    assertEquals(c.toString(), e.toString());
    assertEquals(c.getRDN(), RDN.decode("dc=foo"));
    assertEquals(c.getRDN(0), RDN.decode("dc=foo"));
    assertEquals(c.getRDN(1), RDN.decode("dc=bar"));
    assertEquals(c.getRDN(2), RDN.decode("dc=opends"));
    assertEquals(c.getRDN(3), RDN.decode("dc=org"));
    assertEquals(c.getParent(), DN.decode("dc=bar,dc=opends,dc=org"));
    assertEquals(c.getParent(), e.getParent());
    assertEquals(c.concat(RDN.decode("dc=xxx")), DN
        .decode("dc=xxx,dc=foo,dc=bar,dc=opends,dc=org"));
    assertEquals(c.concat(DN.decode("dc=xxx,dc=yyy")), DN
        .decode("dc=xxx,dc=yyy,dc=foo,dc=bar,dc=opends,dc=org"));
    assertEquals(c.getLocalName(1), DN
        .decode("dc=foo,dc=bar,dc=opends"));
    assertEquals(c.getLocalName(1, 3), DN.decode("dc=bar,dc=opends"));
  }
  /**
   * Concat RDN test data provider.
   *
   * @return The array of test data.
   */
  @DataProvider(name = "createConcatRDNTestData")
  public Object[][] createConcatRDNTestData() {
    return new Object[][] { { "", "dc=org", "dc=org" },
        { "dc=org", "dc=opends", "dc=opends,dc=org" },
        { "dc=opends,dc=org", "dc=foo", "dc=foo,dc=opends,dc=org" }, };
  }
  /**
   * Test the concat(RDN...) method.
   *
   * @param s
   *          The test DN string.
   * @param r
   *          The RDN to be appended.
   * @param e
   *          The expected DN.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createConcatRDNTestData")
  public void testConcatSingleRDN(String s, String r, String e)
      throws Exception {
    DN dn = DN.decode(s);
    RDN rdn = RDN.decode(r);
    DN expected = DN.decode(e);
    assertEquals(dn.concat(rdn), expected);
  }
  /**
   * Test the concat(RDN...) method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testConcatRDNNoOp() throws Exception {
    DN dn = DN.decode("dc=opends,dc=org");
    assertEquals(dn.concat(), dn);
  }
  /**
   * Test the concat(RDN...) method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConcatRDNException() throws Exception {
    DN dn = DN.decode("dc=org");
    dn.concat((RDN[]) null);
  }
  /**
   * Test the concat(RDN[]...) method.
   *
   * @param s
   *          The test DN string.
   * @param l
   *          The local name to be appended.
   * @param e
   *          The expected DN.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createConcatDNTestData")
  public void testConcatRDNSequence1(String s, String l, String e)
      throws Exception {
    DN dn = DN.decode(s);
    DN localName = DN.decode(l);
    DN expected = DN.decode(e);
    // Construct sequence.
    RDN[] rdns = new RDN[localName.getNumComponents()];
    for (int i = 0; i < localName.getNumComponents(); i++) {
      rdns[i] = localName.getRDN(i);
    }
    assertEquals(dn.concat(rdns), expected);
  }
  /**
   * Test the concat(RDN[]...) method.
   *
   * @param s
   *          The test DN string.
   * @param l
   *          The local name to be appended.
   * @param e
   *          The expected DN.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createConcatDNTestData")
  public void testConcatRDNSequence2(String s, String l, String e)
      throws Exception {
    DN dn = DN.decode(s);
    DN localName = DN.decode(l);
    DN expected = DN.decode(e);
    // Construct sequence.
    DN actual = null;
    switch (localName.getNumComponents()) {
    case 0:
      actual = dn.concat();
      break;
    case 1:
      actual = dn.concat(localName.getRDN(0));
      break;
    case 2:
      actual = dn.concat(localName.getRDN(0), localName.getRDN(1));
      break;
    case 3:
      actual = dn.concat(localName.getRDN(0), localName.getRDN(1),
          localName.getRDN(2));
      break;
    case 4:
      actual = dn.concat(localName.getRDN(0), localName.getRDN(1),
          localName.getRDN(3), localName.getRDN(3));
      break;
    }
    assertEquals(actual, expected);
  }
@@ -304,38 +944,427 @@
  /**
   * Test the <CODE>duplicate</CODE> method.
   * Get local name test data provider.
   *
   * @return The array of test data.
   */
  @DataProvider(name = "createGetLocalNameTestData")
  public Object[][] createGetLocalNameTestData() {
    return new Object[][] {
        { "", 0, -1, "" },
        { "", 0, 0, "" },
        { "dc=org", 0, -1, "dc=org" },
        { "dc=org", 1, -1, "" },
        { "dc=org", 0, 0, "" },
        { "dc=org", 0, 1, "dc=org" },
        { "dc=org", 1, 1, "" },
        { "dc=opends,dc=org", 0, -1, "dc=opends,dc=org" },
        { "dc=opends,dc=org", 1, -1, "dc=opends" },
        { "dc=opends,dc=org", 2, -1, "" },
        { "dc=opends,dc=org", 0, 0, "" },
        { "dc=opends,dc=org", 0, 1, "dc=org" },
        { "dc=opends,dc=org", 0, 2, "dc=opends,dc=org" },
        { "dc=opends,dc=org", 1, 1, "" },
        { "dc=opends,dc=org", 1, 2, "dc=opends" },
        { "dc=opends,dc=org", 2, 2, "" },
        { "dc=foo,dc=opends,dc=org", 0, -1, "dc=foo,dc=opends,dc=org" },
        { "dc=foo,dc=opends,dc=org", 1, -1, "dc=foo,dc=opends" },
        { "dc=foo,dc=opends,dc=org", 2, -1, "dc=foo" },
        { "dc=foo,dc=opends,dc=org", 3, -1, "" },
        { "dc=foo,dc=opends,dc=org", 0, 0, "" },
        { "dc=foo,dc=opends,dc=org", 0, 1, "dc=org" },
        { "dc=foo,dc=opends,dc=org", 0, 2, "dc=opends,dc=org" },
        { "dc=foo,dc=opends,dc=org", 0, 3, "dc=foo,dc=opends,dc=org" },
        { "dc=foo,dc=opends,dc=org", 1, 1, "" },
        { "dc=foo,dc=opends,dc=org", 1, 2, "dc=opends" },
        { "dc=foo,dc=opends,dc=org", 1, 3, "dc=foo,dc=opends" },
        { "dc=foo,dc=opends,dc=org", 2, 2, "" },
        { "dc=foo,dc=opends,dc=org", 2, 3, "dc=foo" },
        { "dc=foo,dc=opends,dc=org", 3, 3, "" }, };
  }
  /**
   * Test the getLocalName methods.
   *
   * @param s
   *          The test DN string.
   * @param beginIndex
   *          The index of the uppermost RDN.
   * @param endIndex
   *          The index of the lowest RDN or -1 if there is no end
   *          index.
   * @param e
   *          The expected result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createGetLocalNameTestData")
  public void testGetLocalName(String s, int beginIndex,
      int endIndex, String e) throws Exception {
    DN dn = DN.decode(s);
    DN expected = DN.decode(e);
    if (endIndex < 0) {
      assertEquals(dn.getLocalName(beginIndex), expected);
    } else {
      assertEquals(dn.getLocalName(beginIndex, endIndex), expected);
    }
  }
  /**
   * Test the getLocalName method's interaction with other methods.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testDuplicate() throws Exception
  {
    String s = "dc=example,dc=com";
    DN orig = DN.decode(s);
    DN dup = orig.duplicate();
  public void testGetLocalNameInteraction() throws Exception {
    DN p = DN.decode("dc=foo,dc=bar,dc=opends,dc=org");
    DN e = DN.decode("dc=bar,dc=opends");
    DN l = p.getLocalName(1, 3);
    // The duplicate and the original should compare equal.
    assertEquals(dup, orig);
    assertFalse(l.isNullDN());
    // Alter the duplicate.
    RDN[] origRDNs = dup.getRDNComponents();
    RDN[] dupRDNs = new RDN[origRDNs.length];
    System.arraycopy(origRDNs, 0, dupRDNs, 0, origRDNs.length);
    assertEquals(l.getNumComponents(), 2);
    AttributeValue[] values = new AttributeValue[1];
    values[0] = origRDNs[0].getAttributeValues()[0];
    values[0] = new AttributeValue(origRDNs[0].getAttributeTypes()[0],
                                   new ASN1OctetString("modified"));
    dupRDNs[0] = new RDN(origRDNs[0].getAttributeTypes(),
                         origRDNs[0].getAttributeNames(),
                         values);
    assertEquals(l.compareTo(e), 0);
    assertEquals(e.compareTo(l), 0);
    dup.setRDNComponents(dupRDNs);
    assertTrue(l.isAncestorOf(DN.decode("dc=foo,dc=bar,dc=opends")));
    assertFalse(DN.decode("dc=foo,dc=bar,dc=opends").isAncestorOf(l));
    // Check that the duplicate and the original are different.
    String msg = String.format("<%s> and <%s>", dup, orig);
    assertTrue(!dup.equals(orig), msg);
    assertTrue(l.isDescendantOf(DN.decode("dc=opends")));
    assertFalse(DN.decode("dc=opends").isDescendantOf(l));
    assertEquals(l, e);
    assertEquals(l.hashCode(), e.hashCode());
    assertEquals(l.toNormalizedString(), e.toNormalizedString());
    assertEquals(l.toString(), e.toString());
    assertEquals(l.getRDN(), RDN.decode("dc=bar"));
    assertEquals(l.getRDN(0), RDN.decode("dc=bar"));
    assertEquals(l.getRDN(1), RDN.decode("dc=opends"));
    assertEquals(l.getParent(), DN.decode("dc=opends"));
    assertEquals(l.getParent(), e.getParent());
    assertEquals(l.concat(RDN.decode("dc=xxx")), DN
        .decode("dc=xxx,dc=bar,dc=opends"));
    assertEquals(l.concat(DN.decode("dc=xxx,dc=yyy")), DN
        .decode("dc=xxx,dc=yyy,dc=bar,dc=opends"));
    assertEquals(l.getLocalName(1), DN.decode("dc=bar"));
    assertEquals(l.getLocalName(0, 1), DN.decode("dc=opends"));
  }
  /**
   * Is ancestor of test data provider.
   *
   * @return The array of test data.
   */
  @DataProvider(name = "createIsAncestorOfTestData")
  public Object[][] createIsAncestorOfTestData() {
    return new Object[][] {
        { "", "", true },
        { "", "dc=org", true },
        { "", "dc=opends,dc=org", true },
        { "", "dc=foo,dc=opends,dc=org", true },
        { "dc=org", "", false },
        { "dc=org", "dc=org", true },
        { "dc=org", "dc=opends,dc=org", true },
        { "dc=org", "dc=foo,dc=opends,dc=org", true },
        { "dc=opends,dc=org", "", false },
        { "dc=opends,dc=org", "dc=org", false },
        { "dc=opends,dc=org", "dc=opends,dc=org", true },
        { "dc=opends,dc=org", "dc=foo,dc=opends,dc=org", true },
        { "dc=foo,dc=opends,dc=org", "", false },
        { "dc=foo,dc=opends,dc=org", "dc=org", false },
        { "dc=foo,dc=opends,dc=org", "dc=opends,dc=org", false },
        { "dc=foo,dc=opends,dc=org", "dc=foo,dc=opends,dc=org", true },
        { "dc=org", "dc=com", false },
        { "dc=opends,dc=org", "dc=foo,dc=org", false },
        { "dc=opends,dc=org", "dc=opends,dc=com", false }, };
  }
  /**
   * Test the isAncestoryOf method.
   *
   * @param s
   *          The test DN string.
   * @param d
   *          The dn parameter.
   * @param e
   *          The expected result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createIsAncestorOfTestData")
  public void testIsAncestorOf(String s, String d, boolean e)
      throws Exception {
    DN dn = DN.decode(s);
    DN other = DN.decode(d);
    assertEquals(dn.isAncestorOf(other), e, s + " isAncestoryOf " + d);
  }
  /**
   * Test the isAncestorOf method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testIsAncestorOfException() throws Exception {
    DN dn = DN.decode("dc=com");
    dn.isAncestorOf(null);
  }
  /**
   * Is descendant of test data provider.
   *
   * @return The array of test data.
   */
  @DataProvider(name = "createIsDescendantOfTestData")
  public Object[][] createIsDescendantOfTestData() {
    return new Object[][] {
        { "", "", true },
        { "", "dc=org", false },
        { "", "dc=opends,dc=org", false },
        { "", "dc=foo,dc=opends,dc=org", false },
        { "dc=org", "", true },
        { "dc=org", "dc=org", true },
        { "dc=org", "dc=opends,dc=org", false },
        { "dc=org", "dc=foo,dc=opends,dc=org", false },
        { "dc=opends,dc=org", "", true },
        { "dc=opends,dc=org", "dc=org", true },
        { "dc=opends,dc=org", "dc=opends,dc=org", true },
        { "dc=opends,dc=org", "dc=foo,dc=opends,dc=org", false },
        { "dc=foo,dc=opends,dc=org", "", true },
        { "dc=foo,dc=opends,dc=org", "dc=org", true },
        { "dc=foo,dc=opends,dc=org", "dc=opends,dc=org", true },
        { "dc=foo,dc=opends,dc=org", "dc=foo,dc=opends,dc=org", true },
        { "dc=org", "dc=com", false },
        { "dc=opends,dc=org", "dc=foo,dc=org", false },
        { "dc=opends,dc=org", "dc=opends,dc=com", false }, };
  }
  /**
   * Test the isDescendantOf method.
   *
   * @param s
   *          The test DN string.
   * @param d
   *          The dn parameter.
   * @param e
   *          The expected result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createIsDescendantOfTestData")
  public void testIsDescendantOf(String s, String d, boolean e)
      throws Exception {
    DN dn = DN.decode(s);
    DN other = DN.decode(d);
    assertEquals(dn.isDescendantOf(other), e, s + " isDescendantOf "
        + d);
  }
  /**
   * Test the isDescendantOf method.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testIsDescendantOfException() throws Exception {
    DN dn = DN.decode("dc=com");
    dn.isDescendantOf(null);
  }
  /**
   * DN equality test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "createDNEqualityData")
  public Object[][] createDNEqualityData() {
    return new Object[][] {
        { "cn=hello world,dc=com", "cn=hello world,dc=com", 0 },
        { "cn=hello world,dc=com", "CN=hello world,dc=com", 0 },
        { "cn=hello   world,dc=com", "cn=hello world,dc=com", 0 },
        { "  cn =  hello world  ,dc=com", "cn=hello world,dc=com", 0 },
        { "cn=hello world\\ ,dc=com", "cn=hello world,dc=com", 0 },
        { "cn=HELLO WORLD,dc=com", "cn=hello world,dc=com", 0 },
        { "cn=HELLO+sn=WORLD,dc=com", "sn=world+cn=hello,dc=com", 0 },
        { "x-test-integer-type=10,dc=com",
            "x-test-integer-type=9,dc=com", 1 },
        { "x-test-integer-type=999,dc=com",
            "x-test-integer-type=1000,dc=com", -1 },
        { "x-test-integer-type=-1,dc=com",
            "x-test-integer-type=0,dc=com", -1 },
        { "x-test-integer-type=0,dc=com",
            "x-test-integer-type=-1,dc=com", 1 },
        { "cn=aaa,dc=com", "cn=aaaa,dc=com", -1 },
        { "cn=AAA,dc=com", "cn=aaaa,dc=com", -1 },
        { "cn=aaa,dc=com", "cn=AAAA,dc=com", -1 },
        { "cn=aaaa,dc=com", "cn=aaa,dc=com", 1 },
        { "cn=AAAA,dc=com", "cn=aaa,dc=com", 1 },
        { "cn=aaaa,dc=com", "cn=AAA,dc=com", 1 },
        { "cn=aaab,dc=com", "cn=aaaa,dc=com", 1 },
        { "cn=aaaa,dc=com", "cn=aaab,dc=com", -1 },
        { "dc=aaa,dc=aaa", "dc=bbb", -1 },
        { "dc=bbb,dc=aaa", "dc=bbb", -1 },
        { "dc=ccc,dc=aaa", "dc=bbb", -1 },
        { "dc=aaa,dc=bbb", "dc=bbb", 1 },
        { "dc=bbb,dc=bbb", "dc=bbb", 1 },
        { "dc=ccc,dc=bbb", "dc=bbb", 1 },
        { "dc=aaa,dc=ccc", "dc=bbb", 1 },
        { "dc=bbb,dc=ccc", "dc=bbb", 1 },
        { "dc=ccc,dc=ccc", "dc=bbb", 1 },
    };
  }
  /**
   * Test DN equality
   *
   * @param first
   *          First DN to compare.
   * @param second
   *          Second DN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createDNEqualityData")
  public void testEquality(String first, String second, int result)
      throws Exception {
    DN dn1 = DN.decode(first);
    DN dn2 = DN.decode(second);
    if (result == 0) {
      assertTrue(dn1.equals(dn2), "DN equality for <" + first
          + "> and <" + second + ">");
    } else {
      assertFalse(dn1.equals(dn2), "DN equality for <" + first
          + "> and <" + second + ">");
    }
  }
  /**
   * Test DN hashCode
   *
   * @param first
   *          First DN to compare.
   * @param second
   *          Second DN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createDNEqualityData")
  public void testHashCode(String first, String second, int result)
      throws Exception {
    DN dn1 = DN.decode(first);
    DN dn2 = DN.decode(second);
    int h1 = dn1.hashCode();
    int h2 = dn2.hashCode();
    if (result == 0) {
      if (h1 != h2) {
        fail("Hash codes for <" + first + "> and <" + second
            + "> should be the same.");
      }
    } else {
      if (h1 == h2) {
        fail("Hash codes for <" + first + "> and <" + second
            + "> should be the same.");
      }
    }
  }
  /**
   * Test DN compareTo
   *
   * @param first
   *          First DN to compare.
   * @param second
   *          Second DN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createDNEqualityData")
  public void testCompareTo(String first, String second, int result)
      throws Exception {
    DN dn1 = DN.decode(first);
    DN dn2 = DN.decode(second);
    int rc = dn1.compareTo(dn2);
    // Normalize the result.
    if (rc < 0) {
      rc = -1;
    } else if (rc > 0) {
      rc = 1;
    }
    assertEquals(rc, result, "Comparison for <" + first + "> and <"
        + second + ">.");
  }
  /**
   * Test DN string decoder.
   *
   * @param rawDN
   *          Raw DN string representation.
   * @param normDN
   *          Normalized DN string representation.
   * @param stringDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testDNs")
  public void testToString(String rawDN, String normDN,
      String stringDN) throws Exception {
    DN dn = DN.decode(rawDN);
    assertEquals(dn.toString(), stringDN);
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestEntry.java
@@ -284,7 +284,7 @@
        "{ base \"dc=example, dc=com\", maximum 2 }" };
    // Relative to the root DN.
    DN rootDN = new DN();
    DN rootDN = DN.nullDN();
    SubtreeSpecificationSet expected = new SubtreeSpecificationSet();
    for (String value : values) {
opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
New file
@@ -0,0 +1,869 @@
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at
 * trunk/opends/resource/legal-notices/OpenDS.LICENSE.  If applicable,
 * add the following below this CDDL HEADER, with the fields enclosed
 * by brackets "[]" replaced with your own identifying * information:
 *      Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 *
 *
 *      Portions Copyright 2006 Sun Microsystems, Inc.
 */
package org.opends.server.types;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.fail;
import org.opends.server.TestCaseUtils;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
/**
 * This class defines a set of tests for the
 * {@link org.opends.server.types.RDN} class.
 */
public final class TestRDN extends TypesTestCase {
  // Domain component attribute type.
  private AttributeType AT_DC;
  // Common name attribute type.
  private AttributeType AT_CN;
  // Test attribute value.
  private AttributeValue AV_DC_ORG;
  // Test attribute value.
  private AttributeValue AV_DC_OPENDS;
  // Test attribute value.
  private AttributeValue AV_CN;
  /**
   * Set up the environment for performing the tests in this suite.
   *
   * @throws Exception
   *           If the environment could not be set up.
   */
  @BeforeClass
  public void setUp() throws Exception {
    // This test suite depends on having the schema available, so
    // we'll start the server.
    TestCaseUtils.startServer();
    AT_DC = DirectoryServer.getAttributeType("dc");
    AT_CN = DirectoryServer.getAttributeType("cn");
    AttributeType dummy = DirectoryServer.getDefaultAttributeType(
        "x-test-integer-type", DirectoryServer
            .getDefaultIntegerSyntax());
    DirectoryServer.getSchema().registerAttributeType(dummy, true);
    AV_DC_ORG = new AttributeValue(AT_DC, "org");
    AV_DC_OPENDS = new AttributeValue(AT_DC, "opends");
    AV_CN = new AttributeValue(AT_DC, "hello world");
  }
  // First test the constructors.
  /**
   * Check the constructor throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConstructorNPE1() throws Exception {
    RDN.create(AT_DC, null);
  }
  /**
   * Check the constructor throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConstructorNPE2() throws Exception {
    RDN.create(null, AV_DC_ORG);
  }
  /**
   * Check the constructor throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConstructorNPE3() throws Exception {
    RDN.create(AT_DC, "dc", null);
  }
  /**
   * Check the constructor throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConstructorNPE4() throws Exception {
    RDN.create(AT_DC, null, AV_DC_ORG);
  }
  /**
   * Check the constructor throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testConstructorNPE5() throws Exception {
    RDN.create(null, "dc", AV_DC_ORG);
  }
  /**
   * Check the decode method throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testDecodeNPE() throws Exception {
    RDN.decode((String) null);
  }
  /**
   * Check the valueOf method throws a NPE when parameters are not
   * provided.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = { NullPointerException.class,
      AssertionError.class })
  public void testValueOfNPE() throws Exception {
    RDN.valueOf(null);
  }
  /**
   * Test RDN construction with single AVA.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testConstructor() throws Exception {
    RDN rdn = RDN.create(AT_DC, AV_DC_ORG);
    assertEquals(rdn.getNumValues(), 1);
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeName(0), AT_DC.getNameOrOID());
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
  }
  /**
   * Test RDN construction with single AVA and a user-defined name.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testConstructorWithName() throws Exception {
    RDN rdn = RDN.create(AT_DC, "domainComponent", AV_DC_ORG);
    assertEquals(rdn.getNumValues(), 1);
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeName(0), "domainComponent");
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
  }
  /**
   * Test RDN builder.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testBuilder() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getNumValues(), 1);
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeName(0), AT_DC.getNameOrOID());
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
  }
  /**
   * Test RDN builder.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testBuilderWithName() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, "domainComponent", AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getNumValues(), 1);
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeName(0), "domainComponent");
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
  }
  /**
   * Test RDN builder.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testBuilderIsEmpty() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    assertTrue(builder.isEmpty());
    builder.append(AT_DC, AV_DC_ORG);
    assertFalse(builder.isEmpty());
    builder.getInstance();
    assertFalse(builder.isEmpty());
    builder.clear();
    assertTrue(builder.isEmpty());
  }
  /**
   * Test RDN builder.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = IllegalArgumentException.class)
  public void testBuilderDupesNotAllowed() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    builder.append(AT_DC, AV_DC_OPENDS);
  }
  /**
   * Test RDN builder.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testBuilderMultiAVA() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    builder.append(AT_CN, AV_CN);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getNumValues(), 2);
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeName(0), AT_DC.getNameOrOID());
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
    assertEquals(rdn.getAttributeType(1), AT_CN);
    assertEquals(rdn.getAttributeName(1), AT_CN.getNameOrOID());
    assertEquals(rdn.getAttributeValue(1), AV_CN);
  }
  /**
   * RDN test data provider.
   *
   * @return The array of test RDN strings.
   */
  @DataProvider(name = "testRDNs")
  public Object[][] createData() {
    return new Object[][] {
        { "dc=hello world", "dc=hello world", "dc=hello world" },
        { "DC=HELLO WORLD", "dc=hello world", "DC=HELLO WORLD" },
        { "dc = hello    world", "dc=hello world",
            "dc=hello    world" },
        { "   dc = hello world   ", "dc=hello world",
            "dc=hello world" },
        { "givenName=John+cn=Doe", "cn=doe+givenname=john",
            "givenName=John+cn=Doe" },
        { "givenName=John\\+cn=Doe", "givenname=john\\+cn=doe",
            "givenName=John\\+cn=Doe" },
        { "cn=Doe\\, John", "cn=doe\\, john", "cn=Doe\\, John" },
        { "OU=Sales+CN=J. Smith", "cn=j. smith+ou=sales",
            "OU=Sales+CN=J. Smith" },
        { "CN=James \\\"Jim\\\" Smith\\, III",
            "cn=james \\\"jim\\\" smith\\, iii",
            "CN=James \\\"Jim\\\" Smith\\, III" },
        { "CN=Before\\0dAfter", "cn=before\\0dafter",
            "CN=Before\\0dAfter" },
        { "1.3.6.1.4.1.1466.0=#04024869",
            "1.3.6.1.4.1.1466.0=\\04\\02hi",
            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\\c4\\8di\\c4\\87",
            "CN=Lu\\c4\\8di\\c4\\87" },
        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8",
            "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8",
            "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8" },
        { "photo=\\ john \\ ", "photo=\\ john \\ ",
            "photo=\\ john \\ " },
        { "AB-global=", "ab-global=", "AB-global=" },
        { "cn=John+a=", "a=+cn=john", "cn=John+a=" },
        { "OID.1.3.6.1.4.1.1466.0=#04024869",
            "1.3.6.1.4.1.1466.0=\\04\\02hi",
            "1.3.6.1.4.1.1466.0=\\04\\02Hi" },
        { "O=\"Sue, Grabbit and Runn\"", "o=sue\\, grabbit and runn",
            "O=Sue\\, Grabbit and Runn" }, };
  }
  /**
   * Test RDN string decoder.
   *
   * @param rawRDN
   *          Raw RDN string representation.
   * @param normRDN
   *          Normalized RDN string representation.
   * @param stringRDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testRDNs")
  public void testDecodeString(String rawRDN, String normRDN,
      String stringRDN) throws Exception {
    RDN rdn = RDN.decode(rawRDN);
    assertEquals(rdn.toNormalizedString(), normRDN);
  }
  /**
   * Test RDN byte string decoder.
   *
   * @param rawRDN
   *          Raw RDN string representation.
   * @param normRDN
   *          Normalized RDN string representation.
   * @param stringRDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testRDNs")
  public void testDecodeByteString(String rawRDN, String normRDN,
      String stringRDN) throws Exception {
    ASN1OctetString octetString = new ASN1OctetString(rawRDN);
    RDN rdn = RDN.decode(octetString);
    assertEquals(rdn.toNormalizedString(), normRDN);
  }
  /**
   * Test valueOf.
   *
   * @param rawRDN
   *          Raw RDN string representation.
   * @param normRDN
   *          Normalized RDN string representation.
   * @param stringRDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testRDNs")
  public void testValueOf(String rawRDN, String normRDN,
      String stringRDN) throws Exception {
    RDN rdn = RDN.valueOf(rawRDN);
    assertEquals(rdn.toNormalizedString(), normRDN);
  }
  /**
   * Illegal RDN test data provider.
   *
   * @return The array of illegal test RDN strings.
   */
  @DataProvider(name = "illegalRDNs")
  public Object[][] createIllegalData() {
    return new Object[][] { { "" }, { "=" }, { "manager" },
        { "manager " }, { "cn+Jim" }, { "cn=Jim+" }, { "cn=Jim+sn" },
        { "cn=Jim," }, { "cn=Jim,  " }, { "cn=Jim, sn=Jam " },
        { "cn+uid=Jim" }, { "-cn=Jim" }, { "/tmp=a" }, { "\\tmp=a" },
        { "cn;lang-en=Jim" }, { "@cn=Jim" }, { "_name_=Jim" },
        { "\u03c0=pi" }, { "v1.0=buggy" },
        { "1.3.6.1.4.1.1466..0=#04024869" }, };
  }
  /**
   * Test RDN string decoder against illegal strings.
   *
   * @param rawRDN
   *          Illegal RDN string representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "illegalRDNs", expectedExceptions = DirectoryException.class)
  public void testDecodeString(String rawRDN) throws Exception {
    RDN.decode(rawRDN);
    fail("Expected exception for value \"" + rawRDN + "\"");
  }
  /**
   * Test RDN byte string decoder against illegal strings.
   *
   * @param rawRDN
   *          Illegal RDN string representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "illegalRDNs", expectedExceptions = DirectoryException.class)
  public void testDecodeByteString(String rawRDN) throws Exception {
    ASN1OctetString octetString = new ASN1OctetString(rawRDN);
    RDN.decode(octetString);
    fail("Expected exception for value \"" + rawRDN + "\"");
  }
  /**
   * Test valueOf against illegal strings.
   *
   * @param rawRDN
   *          Illegal RDN string representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "illegalRDNs", expectedExceptions = DirectoryException.class)
  public void testValueOf(String rawRDN) throws Exception {
    RDN.valueOf(rawRDN);
    fail("Expected exception for value \"" + rawRDN + "\"");
  }
  /**
   * Test getAttributeName.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetAttributeName() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    builder.append(AT_CN, AV_CN);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getAttributeName(0), AT_DC.getNameOrOID());
    assertEquals(rdn.getAttributeName(1), AT_CN.getNameOrOID());
  }
  /**
   * Test getAttributeName IndexOutOfBoundsException.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = IndexOutOfBoundsException.class)
  public void testGetAttributeNameException() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    rdn.getAttributeName(1);
  }
  /**
   * Test getAttributeType.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetAttributeType() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    builder.append(AT_CN, AV_CN);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getAttributeType(0), AT_DC);
    assertEquals(rdn.getAttributeType(1), AT_CN);
  }
  /**
   * Test getAttributeType IndexOutOfBoundsException.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = IndexOutOfBoundsException.class)
  public void testGetAttributeTypeException() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    rdn.getAttributeType(1);
  }
  /**
   * Test getAttributeValue.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetAttributeValue() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    builder.append(AT_CN, AV_CN);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
    assertEquals(rdn.getAttributeValue(1), AV_CN);
  }
  /**
   * Test getAttributeValue IndexOutOfBoundsException.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(expectedExceptions = IndexOutOfBoundsException.class)
  public void testGetAttributeValueException() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    rdn.getAttributeValue(1);
  }
  /**
   * Test getAttributeValue.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetAttributeValueByType() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getAttributeValue(AT_DC), AV_DC_ORG);
    assertNull(rdn.getAttributeValue(AT_CN));
  }
  /**
   * Test getNumValues.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testGetNumValues() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertEquals(rdn.getNumValues(), 1);
    builder.append(AT_CN, AV_CN);
    rdn = builder.getInstance();
    assertEquals(rdn.getNumValues(), 2);
  }
  /**
   * Test hasAttributeType.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testHasAttributeType() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertTrue(rdn.hasAttributeType(AT_DC));
    assertFalse(rdn.hasAttributeType(AT_CN));
  }
  /**
   * Test isMultiValued.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testIsMultiValued() throws Exception {
    RDN.Builder builder = RDN.createBuilder();
    builder.append(AT_DC, AV_DC_ORG);
    RDN rdn = builder.getInstance();
    assertFalse(rdn.isMultiValued());
    builder.append(AT_CN, AV_CN);
    rdn = builder.getInstance();
    assertTrue(rdn.isMultiValued());
  }
  /**
   * Test RDN string decoder.
   *
   * @param rawRDN
   *          Raw RDN string representation.
   * @param normRDN
   *          Normalized RDN string representation.
   * @param stringRDN
   *          String representation.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "testRDNs")
  public void testToString(String rawRDN, String normRDN,
      String stringRDN) throws Exception {
    RDN rdn = RDN.decode(rawRDN);
    assertEquals(rdn.toString(), stringRDN);
  }
  /**
   * RDN equality test data provider.
   *
   * @return The array of test RDN strings.
   */
  @DataProvider(name = "createRDNEqualityData")
  public Object[][] createRDNEqualityData() {
    return new Object[][] {
        { "cn=hello world", "cn=hello world", 0 },
        { "cn=hello world", "CN=hello world", 0 },
        { "cn=hello   world", "cn=hello world", 0 },
        { "  cn =  hello world  ", "cn=hello world", 0 },
        { "cn=hello world\\ ", "cn=hello world", 0 },
        { "cn=HELLO WORLD", "cn=hello world", 0 },
        { "cn=HELLO+sn=WORLD", "sn=world+cn=hello", 0 },
        { "x-test-integer-type=10", "x-test-integer-type=9", 1 },
        { "x-test-integer-type=999", "x-test-integer-type=1000", -1 },
        { "x-test-integer-type=-1", "x-test-integer-type=0", -1 },
        { "x-test-integer-type=0", "x-test-integer-type=-1", 1 },
        { "cn=aaa", "cn=aaaa", -1 }, { "cn=AAA", "cn=aaaa", -1 },
        { "cn=aaa", "cn=AAAA", -1 }, { "cn=aaaa", "cn=aaa", 1 },
        { "cn=AAAA", "cn=aaa", 1 }, { "cn=aaaa", "cn=AAA", 1 },
        { "cn=aaab", "cn=aaaa", 1 }, { "cn=aaaa", "cn=aaab", -1 } };
  }
  /**
   * Test RDN equality
   *
   * @param first
   *          First RDN to compare.
   * @param second
   *          Second RDN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testEquality(String first, String second, int result)
      throws Exception {
    RDN rdn1 = RDN.decode(first);
    RDN rdn2 = RDN.decode(second);
    if (result == 0) {
      assertTrue(rdn1.equals(rdn2), "RDN equality for <" + first
          + "> and <" + second + ">");
    } else {
      assertFalse(rdn1.equals(rdn2), "RDN equality for <" + first
          + "> and <" + second + ">");
    }
  }
  /**
   * Test RDN hashCode
   *
   * @param first
   *          First RDN to compare.
   * @param second
   *          Second RDN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testHashCode(String first, String second, int result)
      throws Exception {
    RDN rdn1 = RDN.decode(first);
    RDN rdn2 = RDN.decode(second);
    int h1 = rdn1.hashCode();
    int h2 = rdn2.hashCode();
    if (result == 0) {
      if (h1 != h2) {
        fail("Hash codes for <" + first + "> and <" + second
            + "> should be the same.");
      }
    } else {
      if (h1 == h2) {
        fail("Hash codes for <" + first + "> and <" + second
            + "> should be the same.");
      }
    }
  }
  /**
   * Test RDN compareTo
   *
   * @param first
   *          First RDN to compare.
   * @param second
   *          Second RDN to compare.
   * @param result
   *          Expected comparison result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "createRDNEqualityData")
  public void testCompareTo(String first, String second, int result)
      throws Exception {
    RDN rdn1 = RDN.decode(first);
    RDN rdn2 = RDN.decode(second);
    int rc = rdn1.compareTo(rdn2);
    // Normalize the result.
    if (rc < 0) {
      rc = -1;
    } else if (rc > 0) {
      rc = 1;
    }
    assertEquals(rc, result, "Comparison for <" + first + "> and <"
        + second + ">.");
  }
}
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestAddChangeRecordEntry.java
@@ -83,7 +83,7 @@
  @Test(expectedExceptions = { NullPointerException.class,
                               AssertionError.class })
  public void testConstructorNullDN() throws Exception {
    AddChangeRecordEntry entry = new AddChangeRecordEntry(null, attributes);
    new AddChangeRecordEntry(null, attributes);
  }
  /**
@@ -94,10 +94,10 @@
   */
  @Test
  public void testConstructorEmptyDN() throws Exception {
    AddChangeRecordEntry entry = new AddChangeRecordEntry(new DN(),
    AddChangeRecordEntry entry = new AddChangeRecordEntry(DN.nullDN(),
        attributes);
    Assert.assertEquals(entry.getDN(), new DN());
    Assert.assertEquals(entry.getDN(), DN.nullDN());
  }
  /**
@@ -125,7 +125,7 @@
   */
  @Test
  public void testChangeOperationType() throws Exception {
    AddChangeRecordEntry entry = new AddChangeRecordEntry(new DN(), attributes);
    AddChangeRecordEntry entry = new AddChangeRecordEntry(DN.nullDN(), attributes);
    Assert.assertEquals(entry.getChangeOperationType(),
        ChangeOperationType.ADD);
@@ -140,7 +140,7 @@
  @Test
  public void testGetAttributesEmpty() throws Exception {
    Map<AttributeType, List<Attribute>> empty = Collections.emptyMap();
    AddChangeRecordEntry entry = new AddChangeRecordEntry(new DN(), empty);
    AddChangeRecordEntry entry = new AddChangeRecordEntry(DN.nullDN(), empty);
    List<Attribute> attrs = entry.getAttributes();
    Assert.assertEquals(attrs.size(), 0);
@@ -154,7 +154,7 @@
   */
  @Test
  public void testGetAttributesNonEmpty() throws Exception {
    AddChangeRecordEntry entry = new AddChangeRecordEntry(new DN(), attributes);
    AddChangeRecordEntry entry = new AddChangeRecordEntry(DN.nullDN(), attributes);
    List<Attribute> attrs = entry.getAttributes();
    Assert.assertEquals(attrs.size(), 1);
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestChangeRecordEntry.java
@@ -87,10 +87,9 @@
  @Test(expectedExceptions = { NullPointerException.class,
                               AssertionError.class })
  public void testConstructorNullDN() throws Exception {
    MyChangeRecordEntry entry = new MyChangeRecordEntry(null);
    new MyChangeRecordEntry(null);
  }
  /**
   * Tests the constructor with empty DN.
   *
@@ -99,9 +98,9 @@
   */
  @Test
  public void testConstructorEmptyDN() throws Exception {
    MyChangeRecordEntry entry = new MyChangeRecordEntry(new DN());
    MyChangeRecordEntry entry = new MyChangeRecordEntry(DN.nullDN());
    Assert.assertEquals(entry.getDN(), new DN());
    Assert.assertEquals(entry.getDN(), DN.nullDN());
  }
  /**
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestDeleteChangeRecordEntry.java
@@ -63,7 +63,7 @@
  @Test(expectedExceptions = { NullPointerException.class,
                               AssertionError.class })
  public void testConstructorNullDN() throws Exception {
    DeleteChangeRecordEntry entry = new DeleteChangeRecordEntry(null);
    new DeleteChangeRecordEntry(null);
  }
  /**
@@ -74,9 +74,9 @@
   */
  @Test
  public void testConstructorEmptyDN() throws Exception {
    DeleteChangeRecordEntry entry = new DeleteChangeRecordEntry(new DN());
    DeleteChangeRecordEntry entry = new DeleteChangeRecordEntry(DN.nullDN());
    Assert.assertEquals(entry.getDN(), new DN());
    Assert.assertEquals(entry.getDN(), DN.nullDN());
  }
  /**
@@ -103,7 +103,7 @@
   */
  @Test
  public void testChangeOperationType() throws Exception {
    DeleteChangeRecordEntry entry = new DeleteChangeRecordEntry(new DN());
    DeleteChangeRecordEntry entry = new DeleteChangeRecordEntry(DN.nullDN());
    Assert.assertEquals(entry.getChangeOperationType(),
        ChangeOperationType.DELETE);
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestModifyChangeRecordEntry.java
@@ -85,8 +85,7 @@
  @Test(expectedExceptions = { NullPointerException.class,
                               AssertionError.class })
  public void testConstructorNullDN() throws Exception {
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(null,
        modifications);
    new ModifyChangeRecordEntry(null, modifications);
  }
  /**
@@ -97,10 +96,10 @@
   */
  @Test
  public void testConstructorEmptyDN() throws Exception {
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(new DN(),
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(DN.nullDN(),
        modifications);
    Assert.assertEquals(entry.getDN(), new DN());
    Assert.assertEquals(entry.getDN(), DN.nullDN());
  }
  /**
@@ -128,7 +127,7 @@
   */
  @Test
  public void testChangeOperationType() throws Exception {
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(new DN(),
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(DN.nullDN(),
        modifications);
    Assert.assertEquals(entry.getChangeOperationType(),
@@ -144,7 +143,7 @@
  @Test
  public void testGetModificationsEmpty() throws Exception {
    List<LDAPModification> empty = Collections.emptyList();
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(new DN(),
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(DN.nullDN(),
                                                                empty);
    List<LDAPModification> mods = entry.getModifications();
@@ -159,7 +158,7 @@
   */
  @Test
  public void testGetModificationsNonEmpty() throws Exception {
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(new DN(),
    ModifyChangeRecordEntry entry = new ModifyChangeRecordEntry(DN.nullDN(),
        modifications);
    List<LDAPModification> mods = entry.getModifications();
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestModifyDNChangeRecordEntry.java
@@ -71,7 +71,6 @@
  @Test(expectedExceptions = { NullPointerException.class,
                               AssertionError.class })
  public void testConstructorNullDN() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(null, newRDN, false, newSuperiorDN);
  }
@@ -84,9 +83,9 @@
  @Test
  public void testConstructorEmptyDN() throws Exception {
    ModifyDNChangeRecordEntry entry = new ModifyDNChangeRecordEntry(
        new DN(), newRDN, false, newSuperiorDN);
        DN.nullDN(), newRDN, false, newSuperiorDN);
    Assert.assertEquals(entry.getDN(), new DN());
    Assert.assertEquals(entry.getDN(), DN.nullDN());
  }
  /**
@@ -115,7 +114,7 @@
  @Test
  public void testChangeOperationType() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(new DN(), newRDN, false, newSuperiorDN);
         new ModifyDNChangeRecordEntry(DN.nullDN(), newRDN, false, newSuperiorDN);
    Assert.assertEquals(entry.getChangeOperationType(),
        ChangeOperationType.MODIFY_DN);
@@ -130,9 +129,9 @@
  @Test
  public void testGetNewRDN() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(new DN(), newRDN, false, newSuperiorDN);
         new ModifyDNChangeRecordEntry(DN.nullDN(), newRDN, false, newSuperiorDN);
    Assert.assertEquals(entry.getNewRDN(), newRDN.duplicate());
    Assert.assertEquals(entry.getNewRDN(), newRDN);
  }
  /**
@@ -144,10 +143,10 @@
  @Test
  public void testGetNewSuperiorDN() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(new DN(), newRDN, false, newSuperiorDN);
         new ModifyDNChangeRecordEntry(DN.nullDN(), newRDN, false, newSuperiorDN);
    Assert
        .assertEquals(entry.getNewSuperiorDN(), newSuperiorDN.duplicate());
        .assertEquals(entry.getNewSuperiorDN(), newSuperiorDN);
  }
  /**
@@ -159,7 +158,7 @@
  @Test
  public void testDeleteOldRDNFalse() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(new DN(), newRDN, false, newSuperiorDN);
         new ModifyDNChangeRecordEntry(DN.nullDN(), newRDN, false, newSuperiorDN);
    Assert.assertEquals(entry.deleteOldRDN(), false);
  }
@@ -173,7 +172,7 @@
  @Test
  public void testDeleteOldRDNTrue() throws Exception {
    ModifyDNChangeRecordEntry entry =
         new ModifyDNChangeRecordEntry(new DN(), newRDN, true, newSuperiorDN);
         new ModifyDNChangeRecordEntry(DN.nullDN(), newRDN, true, newSuperiorDN);
    Assert.assertEquals(entry.deleteOldRDN(), true);
  }
opends/tests/unit-tests-testng/src/server/org/opends/server/util/TestStaticUtils.java
@@ -293,6 +293,65 @@
  }
  /**
   * Create test data for {@link StaticUtils#compare(byte[], byte[])}.
   *
   * @return Returns an array of test data.
   */
  @DataProvider(name = "compareBytesTestData")
  public Object[][] createCompareBytesTestData() {
    return new Object[][] {
        { null, null, 0 },
        { null, new byte[0], -1 },
        { new byte[0], null, 1 },
        { new byte[0], new byte[0], 0 },
        { new byte[] { 0x00 }, new byte[] { 0x00 }, 0 },
        { new byte[] { 0x01 }, new byte[] { 0x00 }, 1 },
        { new byte[] { 0x7f }, new byte[] { 0x00 }, 1 },
        { new byte[] { (byte) 0x80 }, new byte[] { 0x00 }, -1 },
        { new byte[] { (byte) 0xff }, new byte[] { 0x00 }, -1 },
        { new byte[] { 0x00 }, new byte[] { 0x01 }, -1 },
        { new byte[] { 0x00 }, new byte[] { 0x7f }, -1 },
        { new byte[] { 0x00 }, new byte[] { (byte) 0x80 }, 1 },
        { new byte[] { 0x00 }, new byte[] { (byte) 0xff }, 1 },
        { new byte[] { 0x00, 0x01, 0x02 },
            new byte[] { 0x00, 0x01, 0x02 }, 0 },
        { new byte[] { 0x00, 0x01 }, new byte[] { 0x00, 0x01, 0x02 },
            -1 },
        { new byte[] { 0x00, 0x01, 0x02 }, new byte[] { 0x00, 0x01 },
            1 }, };
  }
  /**
   * Tests the {@link StaticUtils#compare(byte[], byte[])} method.
   *
   * @param a
   *          The first byte array.
   * @param a2
   *          The second byte array.
   * @param expected
   *          The expected result.
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test(dataProvider = "compareBytesTestData")
  public void testCompareBytes(byte[] a, byte[] a2, int expected)
      throws Exception {
    int rc = StaticUtils.compare(a, a2);
    if (expected < 0 && rc >= 0) {
      Assert.fail("Expected negative result but got " + rc);
    }
    if (expected > 0 && rc <= 0) {
      Assert.fail("Expected positive result but got " + rc);
    }
    if (expected == 0 && rc != 0) {
      Assert.fail("Expected zero result but got " + rc);
    }
  }
  /**
   * Create test strings for the {@link StaticUtils#isDigit(char)}.
   *
   * @return Returns an array of test data.