From b82be51745e0db2395e3ee3c08e28ec3da869e29 Mon Sep 17 00:00:00 2001
From: Nicolas Capponi <nicolas.capponi@forgerock.com>
Date: Mon, 16 Mar 2015 12:52:54 +0000
Subject: [PATCH] OPENDJ-1585 Update DN normalization in SDK to align with server

---
 opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java                                       |   80 ++++++++-------
 opendj-core/src/main/java/org/forgerock/opendj/ldap/AVA.java                                              |    6 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java                                             |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java                                              |   23 ++--
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java      |    2 
 opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java                                               |  133 +++++++++-----------------
 opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java          |    2 
 8 files changed, 106 insertions(+), 144 deletions(-)

diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AVA.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AVA.java
index 6b84fbf..761e67f 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/AVA.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/AVA.java
@@ -799,7 +799,7 @@
      * @param builder
      *            The builder to use to construct the normalized byte string.
      * @return The normalized byte string representation.
-     * @see DN#toIrreversibleNormalizedByteString()
+     * @see DN#toNormalizedByteString()
      */
     ByteStringBuilder toNormalizedByteString(final ByteStringBuilder builder) {
         builder.append(toLowerCase(attributeType.getNameOrOID()));
@@ -819,9 +819,9 @@
      * @param builder
      *            The builder to use to construct the normalized string.
      * @return The normalized readable string representation.
-     * @see DN#toIrreversibleReadableString()
+     * @see DN#toNormalizedUrlSafeString()
      */
-    StringBuilder toNormalizedReadableString(final StringBuilder builder) {
+    StringBuilder toNormalizedUrlSafe(final StringBuilder builder) {
         builder.append(toLowerCase(attributeType.getNameOrOID()));
         builder.append('=');
         final ByteString value = getEqualityNormalizedValue();
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
index 622967f..b19c56b 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
@@ -258,7 +258,8 @@
 
     /**
      * Compares the provided DN values to determine their relative order in a
-     * sorted list.
+     * sorted list. The order is the natural order as defined by the
+     * {@code toNormalizedByteString()} method.
      *
      * @param dn1
      *            The first DN to be compared. It must not be {@code null}.
@@ -270,42 +271,7 @@
      *         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;
-            }
-            // 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--);
-            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;
+        return dn1.toNormalizedByteString().compareTo(dn2.toNormalizedByteString());
     }
 
     /** Decodes a DN using the provided reader and schema. */
@@ -370,6 +336,12 @@
     private final int size;
 
     /**
+     * The normalized byte string representation of this DN, which is not
+     * a valid DN and is not reversible to a valid DN.
+     */
+    private ByteString normalizedDN;
+
+    /**
      * We need to store the original string value if provided in order to
      * preserve the original whitespace.
      */
@@ -497,29 +469,18 @@
     public boolean equals(final Object obj) {
         if (this == obj) {
             return true;
-        } else if (obj instanceof DN) {
-            DN other = (DN) obj;
-            if (size == other.size()) {
-                if (size == 0) {
-                    return true;
-                }
-
-                if (rdn.equals(other.rdn)) {
-                    return parent.equals(other.parent);
-                }
-            }
         }
-
+        if (obj instanceof DN) {
+            DN otherDN = (DN) obj;
+            return toNormalizedByteString().equals(otherDN.toNormalizedByteString());
+        }
         return false;
     }
 
     /** {@inheritDoc} */
     @Override
     public int hashCode() {
-        if (size == 0) {
-            return 0;
-        }
-        return 31 * parent.hashCode() + rdn.hashCode();
+        return toNormalizedByteString().hashCode();
     }
 
     /**
@@ -920,62 +881,60 @@
     }
 
     /**
-     * Returns the irreversible normalized byte string representation of a DN,
-     * suitable for equality and comparisons, and providing a natural hierarchical
-     * ordering, but not usable as a valid DN nor reversible to a valid DN.
+     * Retrieves a normalized byte string representation of this DN.
      * <p>
-     * This representation should be used only when a byte string representation
-     * is needed and when no reversibility to a valid DN is needed. Always consider
-     * using a {@code CompactDn} as an alternative.
+     * This representation is suitable for equality and comparisons, and
+     * for providing a natural hierarchical ordering.
+     * However, it is not a valid DN and can't be reverted to a valid DN.
+     * You should consider using a {@code CompactDn} as an alternative.
      *
-     * @return The normalized byte string representation of the provided DN, not
-     *         usable as a valid DN
+     * @return The normalized string representation of this DN.
      */
-    public ByteString toIrreversibleNormalizedByteString() {
-        if (rdn() == null) {
-            return ByteString.empty();
-        }
-
-        final ByteStringBuilder builder = new ByteStringBuilder();
-        int i = size() - 1;
-        parent(i).rdn().toNormalizedByteString(builder);
-        for (i--; i >= 0; i--) {
-            final RDN rdn = parent(i).rdn();
-            // Only add a separator if the RDN is not RDN.maxValue().
-            if (rdn.size() != 0) {
-                builder.append(DN.NORMALIZED_RDN_SEPARATOR);
+    public ByteString toNormalizedByteString() {
+        if (normalizedDN == null) {
+            if (rdn() == null) {
+                normalizedDN = ByteString.empty();
+            } else {
+                final ByteStringBuilder builder = new ByteStringBuilder();
+                int i = size() - 1;
+                parent(i).rdn().toNormalizedByteString(builder);
+                for (i--; i >= 0; i--) {
+                    final RDN rdn = parent(i).rdn();
+                    // Only add a separator if the RDN is not RDN.maxValue().
+                    if (rdn.size() != 0) {
+                        builder.append(DN.NORMALIZED_RDN_SEPARATOR);
+                    }
+                    rdn.toNormalizedByteString(builder);
+                }
+                normalizedDN = builder.toByteString();
             }
-            rdn.toNormalizedByteString(builder);
         }
-        return builder.toByteString();
+        return normalizedDN;
     }
 
     /**
-     * Returns the irreversible readable string representation of a DN, suitable
-     * for equality and usage as a name in file system or URL, but not usable as
-     * a valid DN nor reversible to a valid DN.
+     * Retrieves a normalized string representation of this DN.
      * <p>
-     * This representation should be used only when a string representation is
-     * needed and when no reversibility to a valid DN is needed.
+     * This representation is safe to use in an URL or in a file name.
+     * However, it is not a valid DN and can't be reverted to a valid DN.
      *
-     * @return The readable string representation of the provided DN,
-     *         not usable as a valid DN
+     * @return The normalized string representation of this DN.
      */
-    public String toIrreversibleReadableString() {
+    public String toNormalizedUrlSafeString() {
         if (rdn() == null) {
             return "";
         }
 
         final StringBuilder builder = new StringBuilder();
         int i = size() - 1;
-        parent(i).rdn().toNormalizedReadableString(builder);
+        parent(i).rdn().toNormalizedUrlSafeString(builder);
         for (i--; i >= 0; i--) {
             final RDN rdn = parent(i).rdn();
             // Only add a separator if the RDN is not RDN.maxValue().
             if (rdn.size() != 0) {
                 builder.append(',');
             }
-            rdn.toNormalizedReadableString(builder);
+            rdn.toNormalizedUrlSafeString(builder);
         }
         return builder.toString();
     }
@@ -1058,7 +1017,7 @@
 
         private byte[] getNormalizedValue() {
             if (normalizedValue == null) {
-                normalizedValue = toDn().toIrreversibleNormalizedByteString().toByteArray();
+                normalizedValue = toDn().toNormalizedByteString().toByteArray();
             }
             return normalizedValue;
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
index eb05a90..80d1337 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
@@ -257,6 +257,7 @@
     }
 
     /** {@inheritDoc} */
+    @Override
     public int compareTo(final RDN rdn) {
         // Identity.
         if (this == rdn) {
@@ -373,6 +374,7 @@
      *
      * @return An iterator of the AVAs contained in this RDN.
      */
+    @Override
     public Iterator<AVA> iterator() {
         return Iterators.arrayIterator(avas);
     }
@@ -421,7 +423,7 @@
      * @param builder
      *            The builder to use to construct the normalized byte string.
      * @return The normalized byte string representation.
-     * @see DN#toIrreversibleNormalizedByteString()
+     * @see DN#toNormalizedByteString()
      */
     ByteStringBuilder toNormalizedByteString(final ByteStringBuilder builder) {
         switch (size()) {
@@ -445,30 +447,29 @@
     }
 
     /**
-     * Returns the normalized readable string representation of this RDN.
+     * Retrieves a normalized string representation of this RDN.
      * <p>
-     * The representation is not a valid RDN.
+     * This representation is safe to use in an URL or in a file name.
+     * However, it is not a valid RDN and can't be reverted to a valid RDN.
      *
-     * @param builder
-     *            The builder to use to construct the normalized string.
-     * @return The normalized readable string representation.
-     * @see DN#toIrreversibleReadableString()
+     * @return The normalized string representation of this RDN.
+     * @see DN#toNormalizedUrlSafeString()
      */
-    StringBuilder toNormalizedReadableString(final StringBuilder builder) {
+    StringBuilder toNormalizedUrlSafeString(final StringBuilder builder) {
         switch (size()) {
         case 0:
             // Handle RDN.maxValue().
             builder.append(RDN.AVA_CHAR_SEPARATOR);
             break;
         case 1:
-            getFirstAVA().toNormalizedReadableString(builder);
+            getFirstAVA().toNormalizedUrlSafe(builder);
             break;
         default:
             Iterator<AVA> it = getSortedAvas();
-            it.next().toNormalizedReadableString(builder);
+            it.next().toNormalizedUrlSafe(builder);
             while (it.hasNext()) {
                 builder.append(RDN.AVA_CHAR_SEPARATOR);
-                it.next().toNormalizedReadableString(builder);
+                it.next().toNormalizedUrlSafe(builder);
             }
             break;
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java
index 480e42c..1e023e6 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/CertificateExactMatchingRuleImpl.java
@@ -187,7 +187,7 @@
     private ByteString normalizeDN(final Schema schema, final String dnstring) throws DecodeException {
         try {
             DN dn = DN.valueOf(dnstring, schema.asNonStrictSchema());
-            return dn.toIrreversibleNormalizedByteString();
+            return dn.toNormalizedByteString();
         } catch (Exception e) {
             logger.traceException(e);
 
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java
index 71a36f5..d8381a3 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleImpl.java
@@ -43,7 +43,7 @@
             throws DecodeException {
         try {
             DN dn = DN.valueOf(value.toString(), schema.asNonStrictSchema());
-            return dn.toIrreversibleNormalizedByteString();
+            return dn.toNormalizedByteString();
         } catch (final LocalizedIllegalArgumentException e) {
             throw DecodeException.error(e.getMessageObject());
         }
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java
index 0297080..951cf84 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldap/schema/UniqueMemberEqualityMatchingRuleImpl.java
@@ -57,7 +57,7 @@
         try {
             DN dn = DN.valueOf(stringValue.substring(0, dnEndPosition), schema.asNonStrictSchema());
             return new ByteStringBuilder()
-                .append(dn.toIrreversibleNormalizedByteString())
+                .append(dn.toNormalizedByteString())
                 .append(optionalUid).toByteString();
         } catch (final LocalizedIllegalArgumentException e) {
             throw DecodeException.error(e.getMessageObject());
diff --git a/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java b/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
index 274ea6b..a16b124 100644
--- a/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
+++ b/opendj-core/src/main/java/org/forgerock/opendj/ldif/LDIF.java
@@ -830,7 +830,7 @@
     }
 
     private static byte[] toNormalizedByteArray(DN dn) {
-        return dn.toIrreversibleNormalizedByteString().toByteArray();
+        return dn.toNormalizedByteString().toByteArray();
     }
 
     private static byte[][] encodeEntry(final Entry entry) {
diff --git a/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java b/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
index 65d539a..e151dab 100644
--- a/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
+++ b/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
@@ -26,6 +26,8 @@
  */
 package org.forgerock.opendj.ldap;
 
+import static java.lang.Integer.*;
+
 import static org.fest.assertions.Assertions.*;
 import static org.testng.Assert.*;
 
@@ -389,8 +391,8 @@
 
         assertEquals(c.size(), 3);
 
-        assertEquals(c.compareTo(p), 1);
-        assertEquals(p.compareTo(c), -1);
+        assertEquals(signum(c.compareTo(p)), 1);
+        assertEquals(signum(p.compareTo(c)), -1);
 
         assertTrue(p.isParentOf(c));
         assertFalse(c.isParentOf(p));
@@ -666,8 +668,8 @@
 
         assertEquals(p.size(), 3);
 
-        assertEquals(p.compareTo(c), -1);
-        assertEquals(c.compareTo(p), 1);
+        assertEquals(signum(p.compareTo(c)), -1);
+        assertEquals(signum(c.compareTo(p)), 1);
 
         assertTrue(p.isParentOf(c));
         assertFalse(c.isParentOf(p));
@@ -1005,12 +1007,12 @@
         assertEquals(actual, "\\#cn\\=foo\\+sn\\=bar");
     }
 
-    /** Tests the {@link DN#toIrreversibleNormalizedByteString()} method. */
-    @Test
-    public void testToIrreversibleNormalizedByteStringWithRootDN() {
-        ByteString actual = DN.rootDN().toIrreversibleNormalizedByteString();
-        assertEquals(actual, ByteString.empty());
-    }
+    /** Tests the {@link DN#toNormalizedByteString()} method. */
+        @Test
+        public void testToNormalizedByteStringWithRootDN() {
+            ByteString actual = DN.rootDN().toNormalizedByteString();
+            assertEquals(actual, ByteString.empty());
+        }
 
     /** Tests the {@link DN#iterator()} method. */
     @Test
@@ -1080,24 +1082,24 @@
     }
 
     @Test(dataProvider = "toIrreversibleNormalizedByteStringDataProvider")
-    public void testToIrreversibleNormalizedByteString(String first, String second, int expectedCompareResult) {
-        DN actual = DN.valueOf(first);
-        DN expected = DN.valueOf(second);
-        int cmp = actual.toIrreversibleNormalizedByteString().compareTo(expected.toIrreversibleNormalizedByteString());
-        assertThat(Integer.signum(cmp)).isEqualTo(expectedCompareResult);
-    }
+        public void testToNormalizedByteString(String first, String second, int expectedCompareResult) {
+            DN actual = DN.valueOf(first);
+            DN expected = DN.valueOf(second);
+            int cmp = actual.toNormalizedByteString().compareTo(expected.toNormalizedByteString());
+            assertThat(signum(cmp)).isEqualTo(expectedCompareResult);
+        }
 
     /** Additional tests with testDNs data provider. */
-    @Test(dataProvider = "testDNs")
-    public void testToIrreversibleNormalizedByteString2(String one, String two, String three) {
-        DN dn1 = DN.valueOf(one);
-        DN dn2 = DN.valueOf(two);
-        DN dn3 = DN.valueOf(three);
-        int cmp = dn1.toIrreversibleNormalizedByteString().compareTo(dn2.toIrreversibleNormalizedByteString());
-        assertThat(cmp).isEqualTo(0);
-        int cmp2 = dn1.toIrreversibleNormalizedByteString().compareTo(dn3.toIrreversibleNormalizedByteString());
-        assertThat(cmp2).isEqualTo(0);
-    }
+        @Test(dataProvider = "testDNs")
+        public void testToNormalizedByteString2(String one, String two, String three) {
+            DN dn1 = DN.valueOf(one);
+            DN dn2 = DN.valueOf(two);
+            DN dn3 = DN.valueOf(three);
+            int cmp = dn1.toNormalizedByteString().compareTo(dn2.toNormalizedByteString());
+            assertThat(cmp).isEqualTo(0);
+            int cmp2 = dn1.toNormalizedByteString().compareTo(dn3.toNormalizedByteString());
+            assertThat(cmp2).isEqualTo(0);
+        }
 
     @DataProvider
     public Object[][] toIrreversibleReadableStringDataProvider() {
@@ -1134,19 +1136,19 @@
     }
 
     @Test(dataProvider = "toIrreversibleReadableStringDataProvider")
-    public void testToIrreversibleReadableString(String dnAsString, String expectedReadableString) {
-        DN actual = DN.valueOf(dnAsString);
-        assertEquals(actual.toIrreversibleReadableString(), expectedReadableString);
-    }
+        public void testToNormalizedUrlSafeString(String dnAsString, String expectedReadableString) {
+            DN actual = DN.valueOf(dnAsString);
+            assertEquals(actual.toNormalizedUrlSafeString(), expectedReadableString);
+        }
 
     /** Additional tests with testDNs data provider. */
-    @Test(dataProvider = "testDNs")
-    public void testToIrreversibleReadableString2(String one, String two, String three) {
-        DN dn1 = DN.valueOf(one);
-        DN dn2 = DN.valueOf(two);
-        DN dn3 = DN.valueOf(three);
-        String irreversibleReadableString = dn1.toIrreversibleReadableString();
-        assertEquals(irreversibleReadableString, dn2.toIrreversibleReadableString());
-        assertEquals(irreversibleReadableString, dn3.toIrreversibleReadableString());
-    }
+        @Test(dataProvider = "testDNs")
+        public void testToNormalizedUrlSafeString2(String one, String two, String three) {
+            DN dn1 = DN.valueOf(one);
+            DN dn2 = DN.valueOf(two);
+            DN dn3 = DN.valueOf(three);
+            String irreversibleReadableString = dn1.toNormalizedUrlSafeString();
+            assertEquals(irreversibleReadableString, dn2.toNormalizedUrlSafeString());
+            assertEquals(irreversibleReadableString, dn3.toNormalizedUrlSafeString());
+        }
 }

--
Gitblit v1.10.0