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

matthew_swift
15.39.2010 d782e4facdae0897de52f560e4ebbf30bd800318
DN normalization enhancements:

* don't expose toNormalizedString since applications should not depend on the normalized representation (and they invariably will mis-use it)

* change normalized form to be big-endian and replace "," and "+" separators with \u0000 and \u0001 respectively in order to provide a more intuitive ordering, especially in the presence of multiple AVAs.
1 files added
6 files modified
799 ■■■■■ changed files
sdk/src/org/opends/sdk/AVA.java 34 ●●●● patch | view | raw | blame | history
sdk/src/org/opends/sdk/DN.java 222 ●●●● patch | view | raw | blame | history
sdk/src/org/opends/sdk/RDN.java 27 ●●●●● patch | view | raw | blame | history
sdk/src/org/opends/sdk/schema/DistinguishedNameEqualityMatchingRuleImpl.java 270 ●●●● patch | view | raw | blame | history
sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java 26 ●●●● patch | view | raw | blame | history
sdk/tests/unit-tests-testng/src/org/opends/sdk/RDNTestCase.java 8 ●●●● patch | view | raw | blame | history
sdk/tests/unit-tests-testng/src/org/opends/sdk/schema/DistinguishedNameEqualityMatchingRuleTest.java 212 ●●●●● patch | view | raw | blame | history
sdk/src/org/opends/sdk/AVA.java
@@ -844,20 +844,6 @@
  StringBuilder toNormalizedString(final StringBuilder builder)
  {
    return toString(builder, true);
  }
  StringBuilder toString(final StringBuilder builder)
  {
    return toString(builder, false);
  }
  private ByteString getNormalizeValue()
  {
    final MatchingRule matchingRule = attributeType.getEqualityMatchingRule();
@@ -877,41 +863,29 @@
  private StringBuilder toString(final StringBuilder builder,
      final boolean normalize)
  StringBuilder toString(final StringBuilder builder)
  {
    final ByteString value = normalize ? getNormalizeValue() : attributeValue;
    if (!attributeType.getNames().iterator().hasNext())
    {
      builder.append(attributeType.getOID());
      builder.append("=#");
      StaticUtils.toHex(value, builder);
      StaticUtils.toHex(attributeValue, builder);
    }
    else
    {
      final String name = attributeType.getNameOrOID();
      if (normalize)
      {
        // Normalizing.
        StaticUtils.toLowerCase(name, builder);
      }
      else
      {
        builder.append(name);
      }
      builder.append("=");
      final Syntax syntax = attributeType.getSyntax();
      if (!syntax.isHumanReadable())
      {
        builder.append("#");
        StaticUtils.toHex(value, builder);
        StaticUtils.toHex(attributeValue, builder);
      }
      else
      {
        final String str = value.toString();
        final String str = attributeValue.toString();
        if (str.length() == 0)
        {
          return builder;
sdk/src/org/opends/sdk/DN.java
@@ -154,6 +154,88 @@
  /**
   * Compares the provided DN values to determine their relative order in a
   * sorted list.
   *
   * @param dn1
   *          The first DN to be compared. It must not be {@code null}.
   * @param dn2
   *          The second DN to be compared. It must not be {@code null}.
   * @return A negative integer if the first DN should come before the second DN
   *         in a sorted list, a positive integer if the first DN should come
   *         after the second DN in a sorted list, or zero if the two DN values
   *         can be considered equal.
   */
  private static int compareTo(final DN dn1, final DN dn2)
  {
    // Quickly check if we are comparing against root dse.
    if (dn1.isRootDN())
    {
      if (dn2.isRootDN())
      {
        // both are equal.
        return 0;
      }
      else
      {
        // dn1 comes before dn2.
        return -1;
      }
    }
    if (dn2.isRootDN())
    {
      // dn1 comes after dn2.
      return 1;
    }
    int dn1Size = dn1.size - 1;
    int dn2Size = dn2.size - 1;
    while (dn1Size >= 0 && dn2Size >= 0)
    {
      final DN dn1Parent = dn1.parent(dn1Size--);
      final DN dn2Parent = dn2.parent(dn2Size--);
      if (dn1Parent.isRootDN())
      {
        if (dn2Parent.isRootDN())
        {
          break;
        }
        return -1;
      }
      if (dn2Parent.isRootDN())
      {
        return 1;
      }
      final int result = dn1Parent.rdn.compareTo(dn2Parent.rdn);
      if (result > 0)
      {
        return 1;
      }
      else if (result < 0)
      {
        return -1;
      }
    }
    // What do we have here?
    if (dn1Size > dn2Size)
    {
      return 1;
    }
    else if (dn1Size < dn2Size)
    {
      return -1;
    }
    return 0;
  }
  // Decodes a DN using the provided reader and schema.
  private static DN decode(final String dnString, final SubstringReader reader,
      final Schema schema, final Map<String, DN> cache)
@@ -237,8 +319,6 @@
  // preserve the original whitespace.
  private String stringValue;
  private String normalizedStringValue = null;
  // Private constructor.
@@ -343,87 +423,6 @@
  /**
   * Compares the provided DN values to determine their relative order in a
   * sorted list.
   *
   * @param dn1
   *          The first DN to be compared. It must not be {@code null}.
   * @param dn2
   *          The second DN to be compared. It must not be {@code null}.
   * @return A negative integer if the first DN should come before the second DN
   *         in a sorted list, a positive integer if the first DN should come
   *         after the second DN in a sorted list, or zero if the two DN values
   *         can be considered equal.
   */
  public int compareTo(final DN dn1, final DN dn2)
  {
    // Quicly check if we are comparing against root dse.
    if (dn1.isRootDN())
    {
      if (dn2.isRootDN())
      {
        // both are equal.
        return 0;
      }
      else
      {
        // dn1 comes before dn2.
        return -1;
      }
    }
    if (dn2.isRootDN())
    {
      // dn1 comes after dn2.
      return 1;
    }
    int dn1Size = dn1.size - 1;
    int dn2Size = dn2.size - 1;
    while (dn1Size >= 0 && dn2Size >= 0)
    {
      final DN dn1Parent = dn1.parent(dn1Size--);
      final DN dn2Parent = dn2.parent(dn2Size--);
      if (dn1Parent.isRootDN())
      {
        if (dn2Parent.isRootDN())
        {
          break;
        }
        return -1;
      }
      if (dn2Parent.isRootDN())
      {
        return 1;
      }
      final int result = dn1Parent.rdn.compareTo(dn2Parent.rdn);
      if (result > 0)
      {
        return 1;
      }
      else if (result < 0)
      {
        return -1;
      }
    }
    // What do we have here?
    if (dn1Size > dn2Size)
    {
      return 1;
    }
    else if (dn1Size < dn2Size)
    {
      return -1;
    }
    return 0;
  }
  /**
   * {@inheritDoc}
   */
  @Override
@@ -435,14 +434,22 @@
    }
    else if (obj instanceof DN)
    {
      final String s1 = toNormalizedString();
      final String s2 = ((DN) obj).toNormalizedString();
      return s1.equals(s2);
    }
    else
      DN other = (DN)obj;
      if(size == other.size())
    {
      return false;
        if(size == 0)
        {
          return true;
    }
        if(rdn.equals(other.rdn))
        {
          return parent.equals(other.parent);
        }
      }
    }
    return false;
  }
@@ -453,8 +460,14 @@
  @Override
  public int hashCode()
  {
    final String s = toNormalizedString();
    return s.hashCode();
    if (size == 0)
    {
      return 0;
    }
    else
    {
      return 31 * parent.hashCode() + rdn.hashCode();
    }
  }
@@ -773,33 +786,6 @@
  /**
   * Returns the normalized string representation of this DN.
   *
   * @return The normalized string representation of this DN.
   */
  public String toNormalizedString()
  {
    if (rdn == null)
    {
      return "".intern();
    }
    if (normalizedStringValue == null)
    {
      final StringBuilder builder = new StringBuilder();
      rdn.toNormalizedString(builder);
      if (!parent.isRootDN())
      {
        builder.append(',');
        builder.append(parent.toNormalizedString());
      }
      normalizedStringValue = builder.toString();
    }
    return normalizedStringValue;
  }
  /**
   * Returns the RFC 4514 string representation of this DN.
   *
   * @return The RFC 4514 string representation of this DN.
sdk/src/org/opends/sdk/RDN.java
@@ -413,33 +413,6 @@
  StringBuilder toNormalizedString(final StringBuilder builder)
  {
    final int sz = avas.length;
    if (sz == 1)
    {
      return avas[0].toNormalizedString(builder);
    }
    else
    {
      // Need to sort the AVAs before comparing.
      final AVA[] a = new AVA[sz];
      System.arraycopy(avas, 0, a, 0, sz);
      Arrays.sort(a);
      // Normalize the first AVA.
      a[0].toNormalizedString(builder);
      for (int i = 1; i < sz; i++)
      {
        builder.append('+');
        a[i].toNormalizedString(builder);
      }
      return builder;
    }
  }
  StringBuilder toString(final StringBuilder builder)
  {
    return builder.append(toString());
sdk/src/org/opends/sdk/schema/DistinguishedNameEqualityMatchingRuleImpl.java
@@ -22,17 +22,18 @@
 * CDDL HEADER END
 *
 *
 *      Copyright 2009 Sun Microsystems, Inc.
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.schema;
import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;
import java.util.Iterator;
import org.opends.sdk.*;
import com.sun.opends.sdk.util.StaticUtils;
import static com.sun.opends.sdk.util.StaticUtils.getBytes;
/**
@@ -42,166 +43,175 @@
final class DistinguishedNameEqualityMatchingRuleImpl extends
    AbstractMatchingRuleImpl
{
  private static final Comparator<AVA> ATV_COMPARATOR = new Comparator<AVA>()
  {
    public int compare(final AVA o1, final AVA o2)
    {
      return o1.getAttributeType().compareTo(o2.getAttributeType());
    }
  };
  @Override
  public Assertion getAssertion(final Schema schema, final ByteSequence value)
      throws DecodeException
  {
    DN assertion;
    try
    {
      assertion = DN.valueOf(value.toString(), schema);
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      throw DecodeException.error(e.getMessageObject());
    }
    final DN finalAssertion = assertion;
    return new Assertion()
    {
      public ConditionResult matches(final ByteSequence attributeValue)
      {
        try
        {
          final DN attribute = DN.valueOf(attributeValue.toString(), schema);
          return matchDNs(finalAssertion, attribute);
        }
        catch (final LocalizedIllegalArgumentException e)
        {
          return ConditionResult.UNDEFINED;
        }
      }
    };
  }
  /**
   * {@inheritDoc}
   */
  public ByteString normalizeAttributeValue(final Schema schema,
      final ByteSequence value) throws DecodeException
  {
    try
    {
      return ByteString.valueOf(DN.valueOf(value.toString(), schema)
          .toNormalizedString());
      DN dn = DN.valueOf(value.toString(), schema.nonStrict());
      StringBuilder builder = new StringBuilder(value.length());
      return ByteString.valueOf(normalizeDN(builder, dn));
    }
    catch (final LocalizedIllegalArgumentException e)
    {
      throw DecodeException.error(e.getMessageObject());
    }
  }
  private ConditionResult matchAVAs(final AVA ava1, final AVA ava2)
    catch (final Exception e)
  {
    final AttributeType type = ava1.getAttributeType();
    if (!type.equals(ava2.getAttributeType()))
    {
      return ConditionResult.FALSE;
    }
    final MatchingRule matchingRule = type.getEqualityMatchingRule();
    if (matchingRule != null)
    {
      try
      {
        final ByteString nv1 = matchingRule.normalizeAttributeValue(ava1
            .getAttributeValue());
        final ByteString nv2 = matchingRule.normalizeAttributeValue(ava2
            .getAttributeValue());
        return nv1.equals(nv2) ? ConditionResult.TRUE : ConditionResult.FALSE;
      }
      catch (final DecodeException de)
      {
        return ConditionResult.UNDEFINED;
      throw DecodeException.error(LocalizableMessage.raw(e.toString()));
      }
    }
    return ConditionResult.UNDEFINED;
  /**
   * Returns the normalized string representation of a DN.
   *
   * @param builder The StringBuilder to use to construct the normalized string.
   * @param dn The DN.
   * @return The normalized string representation of the provided DN.
   */
  private static StringBuilder normalizeDN(final StringBuilder builder,
                                          final DN dn)
  {
    if(dn.rdn() == null)
    {
      return builder;
  }
  private ConditionResult matchDNs(final DN dn1, final DN dn2)
    int i = dn.size() - 1;
    normalizeRDN(builder, dn.parent(i).rdn());
    for (i--; i >= 0; i--)
  {
    final int sz1 = dn1.size();
    final int sz2 = dn2.size();
    if (sz1 != sz2)
    {
      return ConditionResult.FALSE;
      builder.append('\u0000');
      normalizeRDN(builder, dn.parent(i).rdn());
    }
    else
    {
      final RDN rdn1 = dn1.rdn();
      final RDN rdn2 = dn2.rdn();
      while (rdn1 != null)
      {
        final ConditionResult result = matchRDNs(rdn1, rdn2);
        if (result != ConditionResult.TRUE)
        {
          return result;
        }
      }
      return ConditionResult.TRUE;
    }
    return builder;
  }
  private ConditionResult matchRDNs(final RDN rdn1, final RDN rdn2)
  /**
   * Returns the normalized string representation of a RDN.
   *
   * @param builder The StringBuilder to use to construct the normalized string.
   * @param rdn The RDN.
   * @return The normalized string representation of the provided RDN.
   */
  private static StringBuilder normalizeRDN(final StringBuilder builder,
                                           final RDN rdn)
  {
    final int sz1 = rdn1.size();
    final int sz2 = rdn2.size();
    if (sz1 != sz2)
    final int sz = rdn.size();
    if (sz == 1)
    {
      return ConditionResult.FALSE;
    }
    else if (sz1 == 1)
    {
      return matchAVAs(rdn1.getFirstAVA(), rdn2.getFirstAVA());
      return normalizeAVA(builder, rdn.getFirstAVA());
    }
    else
    {
      // Need to sort the AVAs before comparing.
      final AVA[] a1 = new AVA[sz1];
      int i = 0;
      for (final AVA ava : rdn1)
      TreeSet<AVA> a = new TreeSet<AVA>();
      for(AVA ava : rdn)
      {
        a1[i++] = ava;
        a.add(ava);
      }
      Arrays.sort(a1, ATV_COMPARATOR);
      final AVA[] a2 = new AVA[sz1];
      i = 0;
      for (final AVA ava : rdn2)
      Iterator<AVA> i = a.iterator();
      // Normalize the first AVA.
      normalizeAVA(builder, i.next());
      while(i.hasNext())
      {
        a2[i++] = ava;
        builder.append('\u0001');
        normalizeAVA(builder, i.next());
      }
      Arrays.sort(a2, ATV_COMPARATOR);
      for (i = 0; i < sz1; i++)
      {
        final ConditionResult result = matchAVAs(a1[i], a2[i]);
        if (result != ConditionResult.TRUE)
        {
          return result;
      return builder;
        }
      }
      return ConditionResult.TRUE;
  /**
   * Returns the normalized string representation of an AVA.
   *
   * @param builder The StringBuilder to use to construct the normalized string.
   * @param ava The AVA.
   * @return The normalized string representation of the provided AVA.
   */
  private static StringBuilder normalizeAVA(final StringBuilder builder,
                                           final AVA ava)
  {
    ByteString value = ava.getAttributeValue();
    final MatchingRule matchingRule =
        ava.getAttributeType().getEqualityMatchingRule();
    if (matchingRule != null)
    {
      try
      {
        value =
            matchingRule.normalizeAttributeValue(ava.getAttributeValue());
    }
      catch (final DecodeException de)
      {
        // Ignore - we'll drop back to the user provided value.
      }
    }
    if (!ava.getAttributeType().getNames().iterator().hasNext())
    {
      builder.append(ava.getAttributeType().getOID());
      builder.append("=#");
      StaticUtils.toHex(value, builder);
    }
    else
    {
      final String name = ava.getAttributeType().getNameOrOID();
      // Normalizing.
      StaticUtils.toLowerCase(name, builder);
      builder.append("=");
      final Syntax syntax = ava.getAttributeType().getSyntax();
      if (!syntax.isHumanReadable())
      {
        builder.append("#");
        StaticUtils.toHex(value, builder);
      }
      else
      {
        final String str = value.toString();
        if (str.length() == 0)
        {
          return builder;
        }
        char c = str.charAt(0);
        int startPos = 0;
        if ((c == ' ') || (c == '#'))
        {
          builder.append('\\');
          builder.append(c);
          startPos = 1;
        }
        final int length = str.length();
        for (int si = startPos; si < length; si++)
        {
          c = str.charAt(si);
          if (c < ' ')
          {
            for (final byte b : getBytes(String.valueOf(c)))
            {
              builder.append('\\');
              builder.append(StaticUtils.byteToLowerHex(b));
            }
          }
          else
          {
            if ((c == ' ' && si == length - 1)
                || (c == '"' || c == '+' || c == ',' || c == ';' || c == '<'
                    || c == '=' || c == '>' || c == '\\' || c == '\u0000'))
            {
              builder.append('\\');
            }
            builder.append(c);
          }
        }
      }
    }
    return builder;
  }
}
sdk/tests/unit-tests-testng/src/org/opends/sdk/DNTestCase.java
@@ -486,7 +486,6 @@
    assertEquals(c, e);
    assertEquals(c.hashCode(), e.hashCode());
    assertEquals(c.toNormalizedString(), e.toNormalizedString());
    assertEquals(c.toString(), e.toString());
    assertEquals(c.rdn(), RDN.valueOf("dc=foo"));
@@ -799,7 +798,6 @@
    assertEquals(p, e);
    assertEquals(p.hashCode(), e.hashCode());
    assertEquals(p.toNormalizedString(), e.toNormalizedString());
    assertEquals(p.toString(), e.toString());
    assertEquals(p.rdn(), RDN.valueOf("dc=bar"));
@@ -885,7 +883,6 @@
    final DN nullDN = DN.rootDN();
    assertTrue(nullDN.isRootDN());
    assertTrue(nullDN.size() == 0);
    assertEquals(nullDN.toNormalizedString(), "");
  }
@@ -936,21 +933,6 @@
  /**
   * Tests the toNoramlizedString methods.
   *
   * @throws Exception
   *           If the test failed unexpectedly.
   */
  @Test
  public void testToNormalizedString() throws Exception
  {
    final DN dn = DN.valueOf("dc=example,dc=com");
    assertEquals(dn.toNormalizedString(), "dc=example,dc=com");
  }
  /**
   * Test the RFC 4514 string representation of the DN.
   *
   * @param rawDN
@@ -988,10 +970,8 @@
  public void testValueOfString(final String rawDN, final String normDN,
      final String stringDN) throws Exception
  {
    final DN dn = DN.valueOf(rawDN);
    final StringBuilder buffer = new StringBuilder();
    buffer.append(normDN);
    Platform.normalize(buffer);
    assertEquals(dn.toNormalizedString(), buffer.toString());
    final DN raw = DN.valueOf(rawDN);
    final DN string = DN.valueOf(stringDN);
    assertEquals(raw, string);
  }
}
sdk/tests/unit-tests-testng/src/org/opends/sdk/RDNTestCase.java
@@ -325,12 +325,8 @@
      final String stringRDN) throws Exception
  {
    final RDN rdn = RDN.valueOf(rawRDN);
    final StringBuilder buffer = new StringBuilder();
    buffer.append(normRDN);
    Platform.normalize(buffer);
    final StringBuilder normalBuffer = new StringBuilder();
    rdn.toNormalizedString(normalBuffer);
    assertEquals(normalBuffer.toString(), buffer.toString());
    final RDN string = RDN.valueOf(stringRDN);
    assertEquals(rdn, string);
  }
sdk/tests/unit-tests-testng/src/org/opends/sdk/schema/DistinguishedNameEqualityMatchingRuleTest.java
New file
@@ -0,0 +1,212 @@
/*
 * 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
 *
 *
 *      Copyright 2009-2010 Sun Microsystems, Inc.
 */
package org.opends.sdk.schema;
import org.testng.annotations.Test;
import org.testng.annotations.DataProvider;
import static org.testng.Assert.assertEquals;
import org.opends.sdk.ConditionResult;
import org.opends.sdk.ByteString;
import static org.opends.sdk.schema.SchemaConstants.EMR_DN_OID;
import com.sun.opends.sdk.util.Platform;
/**
 * Test the DistinguishedNameEqualityMatchingRule
 */
public class DistinguishedNameEqualityMatchingRuleTest extends MatchingRuleTest
{
  @DataProvider(name = "matchingRuleInvalidAttributeValues")
  public Object[][] createMatchingRuleInvalidAttributeValues()
  {
    return new Object[][] { { "manager" }, { "manager " }, { "=Jim" },
        { " =Jim" }, { "= Jim" },
        { " = Jim" },
        { "cn+Jim" },
        { "cn + Jim" },
        { "cn=Jim+" },
        { "cn=Jim+manager" },
        { "cn=Jim+manager " },
        { "cn=Jim+manager," },// { "cn=Jim," }, { "cn=Jim,  " }, { "c[n]=Jim" },
        { "_cn=Jim" }, { "c_n=Jim" }, { "cn\"=Jim" }, { "c\"n=Jim" },
        { "1cn=Jim" }, { "cn+uid=Jim" }, { "-cn=Jim" }, { "/tmp=a" },
        { "\\tmp=a" }, { "cn;lang-en=Jim" }, { "@cn=Jim" },
        { "_name_=Jim" },
        { "\u03c0=pi" },
        { "v1.0=buggy" },// { "1.=buggy" }, { ".1=buggy" },
        { "oid.1." }, { "1.3.6.1.4.1.1466..0=#04024869" }, { "cn=#a" },
        { "cn=#ag" }, { "cn=#ga" }, { "cn=#abcdefgh" },
        { "cn=a\\b" }, // { "cn=a\\bg" }, { "cn=\"hello" },
        { "cn=+mail=,dc=example,dc=com" }, { "cn=xyz+sn=,dc=example,dc=com" },
        { "cn=,dc=example,dc=com" } };
  }
  @DataProvider(name = "matchingrules")
  public Object[][] createMatchingRuleTest()
  {
    return new Object[][] {
        { "", "", ConditionResult.TRUE },
        { "   ", "", ConditionResult.TRUE },
        { "cn=", "cn=", ConditionResult.TRUE },
        { "cn= ", "cn=", ConditionResult.TRUE },
        { "cn =", "cn=", ConditionResult.TRUE },
        { "cn = ", "cn=", ConditionResult.TRUE },
        { "dc=com", "dc=com", ConditionResult.TRUE },
        { "dc=com+o=com", "dc=com+o=com", ConditionResult.TRUE },
        { "DC=COM", "dc=com", ConditionResult.TRUE },
        { "dc = com", "dc=com", ConditionResult.TRUE },
        { " dc = com ", "dc=com", ConditionResult.TRUE },
        { "dc=example,dc=com", "dc=example,dc=com", ConditionResult.TRUE },
        { "dc=example, dc=com", "dc=example,dc=com", ConditionResult.TRUE },
        { "dc=example ,dc=com", "dc=example,dc=com", ConditionResult.TRUE },
        { "dc =example , dc  =   com", "dc=example,dc=com",
          ConditionResult.TRUE },
        { "givenName=John+cn=Doe,ou=People,dc=example,dc=com",
            "cn=doe+givenname=john,ou=people,dc=example,dc=com",
            ConditionResult.TRUE },
        { "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com",
            "givenname=john\\+cn\\=doe,ou=people,dc=example,dc=com",
            ConditionResult.TRUE },
        { "cn=Doe\\, John,ou=People,dc=example,dc=com",
            "cn=doe\\, john,ou=people,dc=example,dc=com", ConditionResult.TRUE },
        { "UID=jsmith,DC=example,DC=net", "uid=jsmith,dc=example,dc=net",
          ConditionResult.TRUE },
        { "OU=Sales+CN=J. Smith,DC=example,DC=net",
            "cn=j. smith+ou=sales,dc=example,dc=net", ConditionResult.TRUE },
        { "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
            "cn=james \\\"jim\\\" smith\\, iii,dc=example,dc=net",
            ConditionResult.TRUE },
        { "CN=John Smith\\2C III,DC=example,DC=net",
            "cn=john smith\\, iii,dc=example,dc=net", ConditionResult.TRUE },
        { "CN=\\23John Smith\\20,DC=example,DC=net",
            "cn=\\#john smith,dc=example,dc=net", ConditionResult.TRUE },
        {
            "CN=Before\\0dAfter,DC=example,DC=net",
            // \0d is a hex representation of Carriage return. It is mapped
            // to a SPACE as defined in the MAP ( RFC 4518)
            "cn=before after,dc=example,dc=net", ConditionResult.TRUE },
        { "2.5.4.3=#04024869",
        // Unicode codepoints from 0000-0008 are mapped to nothing.
            "cn=hi", ConditionResult.TRUE },
        { "1.1.1=", "1.1.1=", ConditionResult.TRUE },
        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\u010di\u0107",
          ConditionResult.TRUE },
        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius",
            "ou=\u55b6\u696d\u90e8,o=airius", ConditionResult.TRUE },
        { "photo=\\ john \\ ,dc=com", "photo=\\ john \\ ,dc=com",
          ConditionResult.TRUE },
        { "AB-global=", "ab-global=", ConditionResult.TRUE },
        { "OU= Sales + CN = J. Smith ,DC=example,DC=net",
            "cn=j. smith+ou=sales,dc=example,dc=net", ConditionResult.TRUE },
        { "cn=John+a=Doe", "a=Doe+cn=john", ConditionResult.TRUE },
        { "O=\"Sue, Grabbit and Runn\",C=US", "o=sue\\, grabbit and runn,c=us",
          ConditionResult.TRUE }, };
  }
   /**
   * DN test data provider.
   *
   * @return The array of test DN strings.
   */
  @DataProvider(name = "testDNs")
  public Object[][] createData()
  {
    return new Object[][] {
        { "", ""},
        { "   ", ""},
        { "cn=", "cn="},
        { "cn= ", "cn="},
        { "cn =", "cn="},
        { "cn = ", "cn="},
        { "dc=com", "dc=com"},
        { "dc=com+o=com", "dc=com\u0001o=com"},
        { "DC=COM", "dc=com"},
        { "dc = com", "dc=com"},
        { " dc = com ", "dc=com"},
        { "dc=example,dc=com", "dc=com\u0000dc=example"},
        { "dc=example, dc=com", "dc=com\u0000dc=example"},
        { "dc=example ,dc=com", "dc=com\u0000dc=example"},
        { "dc =example , dc  =   com", "dc=com\u0000dc=example"},
        { "givenName=John+cn=Doe,ou=People,dc=example,dc=com",
            "dc=com\u0000dc=example\u0000ou=people\u0000cn=doe\u0001givenname=john"},
        { "givenName=John\\+cn=Doe,ou=People,dc=example,dc=com",
            "dc=com\u0000dc=example\u0000ou=people\u0000givenname=john\\+cn\\=doe"},
        { "cn=Doe\\, John,ou=People,dc=example,dc=com",
            "dc=com\u0000dc=example\u0000ou=people\u0000cn=doe\\, john"},
        { "UID=jsmith,DC=example,DC=net", "dc=net\u0000dc=example\u0000uid=jsmith"},
        { "OU=Sales+CN=J. Smith,DC=example,DC=net",
            "dc=net\u0000dc=example\u0000cn=j. smith\u0001ou=sales"},
        { "CN=James \\\"Jim\\\" Smith\\, III,DC=example,DC=net",
            "dc=net\u0000dc=example\u0000cn=james \\\"jim\\\" smith\\, iii"},
        { "CN=John Smith\\2C III,DC=example,DC=net",
            "dc=net\u0000dc=example\u0000cn=john smith\\, iii"},
        { "CN=\\23John Smith\\20,DC=example,DC=net",
            "dc=net\u0000dc=example\u0000cn=\\#john smith"},
        {
            "CN=Before\\0dAfter,DC=example,DC=net",
            // \0d is a hex representation of Carriage return. It is mapped
            // to a SPACE as defined in the MAP ( RFC 4518)
            "dc=net\u0000dc=example\u0000cn=before after"},
        { "2.5.4.3=#04024869",
        // Unicode codepoints from 0000-0008 are mapped to nothing.
            "cn=hi"},
        { "1.1.1=", "1.1.1="},
        { "CN=Lu\\C4\\8Di\\C4\\87", "cn=lu\u010di\u0107"},
        { "ou=\\e5\\96\\b6\\e6\\a5\\ad\\e9\\83\\a8,o=Airius",
            "o=airius\u0000ou=\u55b6\u696d\u90e8"},
        { "photo=\\ john \\ ,dc=com", "dc=com\u0000photo=\\ john \\ "},
        { "AB-global=", "ab-global="},
        { "OU= Sales + CN = J. Smith ,DC=example,DC=net",
            "dc=net\u0000dc=example\u0000cn=j. smith\u0001ou=sales"},
        { "cn=John+a=", "a=\u0001cn=john"},
        { "O=\"Sue, Grabbit and Runn\",C=US",
          "c=us\u0000o=sue\\, grabbit and runn" }, };
  }
  protected MatchingRule getRule()
  {
    return Schema.getCoreSchema().getMatchingRule(EMR_DN_OID);
  }
  /**
   * Test the normalized values
   */
  @Test(dataProvider = "testDNs")
  public void matchingRules(final String value1, final String value2)
      throws Exception
  {
    final MatchingRule rule = getRule();
    final ByteString normalizedValue1 = rule.normalizeAttributeValue(ByteString
        .valueOf(value1));
    StringBuilder buffer = new StringBuilder(value2);
    Platform.normalize(buffer);
    final ByteString expectedValue = ByteString.valueOf(buffer);
    assertEquals(normalizedValue1, expectedValue);
  }
}