From 42dd9eb626b81e5f370f3d66f932a72fc2bdc0d5 Mon Sep 17 00:00:00 2001
From: Jean-Noël Rouvignac <jean-noel.rouvignac@forgerock.com>
Date: Mon, 07 Mar 2016 15:44:49 +0000
Subject: [PATCH] OPENDJ-1342 Migrate AVA, RDN, and DN classes
---
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java | 106 ++++++++++++++---
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java | 9 +
opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java | 105 +++++++++++------
opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java | 139 ++++++++++++++++++++--
4 files changed, 285 insertions(+), 74 deletions(-)
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
index 5956133..d93d03f 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/DN.java
@@ -21,6 +21,7 @@
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.UUID;
import java.util.WeakHashMap;
import org.forgerock.i18n.LocalizableMessage;
@@ -56,6 +57,9 @@
static final byte NORMALIZED_AVA_SEPARATOR = 0x01;
static final byte NORMALIZED_ESC_BYTE = 0x02;
+ static final char RDN_CHAR_SEPARATOR = ',';
+ static final char AVA_CHAR_SEPARATOR = '+';
+
private static final DN ROOT_DN = new DN(CoreSchema.getInstance(), null, null, "");
/**
@@ -191,15 +195,13 @@
}
/**
- * Parses the provided LDAP string representation of a DN using the default
- * schema.
+ * Parses the provided LDAP string representation of a DN using the default schema.
*
* @param dn
* The LDAP string representation of a DN.
* @return The parsed DN.
* @throws LocalizedIllegalArgumentException
- * If {@code dn} is not a valid LDAP string representation of a
- * DN.
+ * If {@code dn} is not a valid LDAP string representation of a DN.
* @throws NullPointerException
* If {@code dn} was {@code null}.
* @see #format(String, Object...)
@@ -209,8 +211,7 @@
}
/**
- * Parses the provided LDAP string representation of a DN using the provided
- * schema.
+ * Parses the provided LDAP string representation of a DN using the provided schema.
*
* @param dn
* The LDAP string representation of a DN.
@@ -218,8 +219,7 @@
* The schema to use when parsing the DN.
* @return The parsed DN.
* @throws LocalizedIllegalArgumentException
- * If {@code dn} is not a valid LDAP string representation of a
- * DN.
+ * If {@code dn} is not a valid LDAP string representation of a DN.
* @throws NullPointerException
* If {@code dn} or {@code schema} was {@code null}.
* @see #format(String, Schema, Object...)
@@ -243,21 +243,18 @@
}
/**
- * Compares the provided DN values to determine their relative order in a
- * sorted list. The order is the natural order as defined by the
- * {@code toNormalizedByteString()} method.
+ * Parses the provided LDAP string representation of a DN using the default schema.
*
- * @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.
+ * @param dn
+ * The LDAP byte string representation of a DN.
+ * @return The parsed DN.
+ * @throws LocalizedIllegalArgumentException
+ * If {@code dn} is not a valid LDAP byte string representation of a DN.
+ * @throws NullPointerException
+ * If {@code dn} was {@code null}.
*/
- private static int compareTo(final DN dn1, final DN dn2) {
- return dn1.toNormalizedByteString().compareTo(dn2.toNormalizedByteString());
+ public static DN valueOf(ByteString dn) {
+ return DN.valueOf(dn.toString());
}
/** Decodes a DN using the provided reader and schema. */
@@ -446,7 +443,7 @@
@Override
public int compareTo(final DN dn) {
- return compareTo(this, dn);
+ return toNormalizedByteString().compareTo(dn.toNormalizedByteString());
}
@Override
@@ -814,6 +811,29 @@
}
/**
+ * Returns the RDN at the specified index for this DN,
+ * or {@code null} if no such RDN exists.
+ * <p>
+ * Here is an example usage:
+ * <pre>
+ * DN.valueOf("ou=people,dc=example,dc=com").rdn(0) => "ou=people"
+ * DN.valueOf("ou=people,dc=example,dc=com").rdn(1) => "dc=example"
+ * DN.valueOf("ou=people,dc=example,dc=com").rdn(2) => "dc=com"
+ * DN.valueOf("ou=people,dc=example,dc=com").rdn(3) => null
+ * </pre>
+ *
+ * @param index
+ * The index of the requested RDN.
+ * @return The RDN at the specified index, or {@code null} if no such RDN exists.
+ * @throws IllegalArgumentException
+ * If {@code index} is less than zero.
+ */
+ public RDN rdn(int index) {
+ DN parentDN = parent(index);
+ return parentDN != null ? parentDN.rdn : null;
+ }
+
+ /**
* Returns a copy of this DN whose parent DN, {@code fromDN}, has been
* renamed to the new parent DN, {@code toDN}. If this DN is not subordinate
* or equal to {@code fromDN} then this DN is returned (i.e. the DN is not
@@ -863,7 +883,7 @@
final StringBuilder builder = new StringBuilder();
rdn.toString(builder);
if (!parent.isRootDN()) {
- builder.append(',');
+ builder.append(RDN_CHAR_SEPARATOR);
builder.append(parent);
}
stringValue = builder.toString();
@@ -883,20 +903,13 @@
*/
public ByteString toNormalizedByteString() {
if (normalizedDN == null) {
- if (rdn() == 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.appendByte(DN.NORMALIZED_RDN_SEPARATOR);
- }
- rdn.toNormalizedByteString(builder);
- }
+ final ByteString normalizedParent = parent.toNormalizedByteString();
+ final ByteStringBuilder builder = new ByteStringBuilder(normalizedParent.length() + 16);
+ builder.appendBytes(normalizedParent);
+ rdn.toNormalizedByteString(builder);
normalizedDN = builder.toByteString();
}
}
@@ -916,14 +929,17 @@
return "";
}
+ // This code differs from toNormalizedByteString(),
+ // because we do not care about ordering comparisons.
+ // (so we do not append the RDN_SEPARATOR first)
final StringBuilder builder = new StringBuilder();
int i = size() - 1;
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().
+ // Only add a separator if the RDN is not RDN.maxValue() or RDN.minValue().
if (rdn.size() != 0) {
- builder.append(',');
+ builder.append(RDN_CHAR_SEPARATOR);
}
rdn.toNormalizedUrlSafeString(builder);
}
@@ -931,6 +947,21 @@
}
/**
+ * Returns a UUID whose content is based on the normalized content of this DN.
+ * Two equivalent DNs subject to the same schema will always yield the same UUID.
+ *
+ * @return the UUID representing this DN
+ */
+ public UUID toUUID() {
+ ByteString normDN = toNormalizedByteString();
+ if (!normDN.isEmpty()) {
+ // remove leading RDN separator
+ normDN = normDN.subSequence(1, normDN.length());
+ }
+ return UUID.nameUUIDFromBytes(normDN.toByteArray());
+ }
+
+ /**
* A compact representation of a DN, suitable for equality and comparisons, and providing a natural hierarchical
* ordering.
* <p>
diff --git a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
index aef0808..85854ac 100644
--- a/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
+++ b/opendj-sdk/opendj-core/src/main/java/org/forgerock/opendj/ldap/RDN.java
@@ -16,6 +16,11 @@
*/
package org.forgerock.opendj.ldap;
+import static org.forgerock.opendj.ldap.DN.AVA_CHAR_SEPARATOR;
+import static org.forgerock.opendj.ldap.DN.RDN_CHAR_SEPARATOR;
+import static org.forgerock.opendj.ldap.DN.NORMALIZED_AVA_SEPARATOR;
+import static org.forgerock.opendj.ldap.DN.NORMALIZED_RDN_SEPARATOR;
+
import static com.forgerock.opendj.ldap.CoreMessages.ERR_RDN_TYPE_NOT_FOUND;
import java.util.ArrayList;
@@ -30,10 +35,10 @@
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.forgerock.opendj.ldap.schema.Schema;
import org.forgerock.opendj.ldap.schema.UnknownSchemaElementException;
+import org.forgerock.util.Reject;
import com.forgerock.opendj.util.Iterators;
import com.forgerock.opendj.util.SubstringReader;
-import org.forgerock.util.Reject;
/**
* A relative distinguished name (RDN) as defined in RFC 4512 section 2.3 is the
@@ -63,18 +68,44 @@
*/
public final class RDN implements Iterable<AVA>, Comparable<RDN> {
- /** Separator for AVAs. */
- private static final char AVA_CHAR_SEPARATOR = '+';
-
/**
- * A constant holding a special RDN having zero AVAs and which always
- * compares greater than any other RDN other than itself.
+ * A constant holding a special RDN having zero AVAs
+ * and which sorts before any RDN other than itself.
+ */
+ private static final RDN MIN_VALUE = new RDN(new AVA[0], "");
+ /**
+ * A constant holding a special RDN having zero AVAs
+ * and which sorts after any RDN other than itself.
*/
private static final RDN MAX_VALUE = new RDN(new AVA[0], "");
/**
- * Returns a constant containing a special RDN which is greater than any
- * other RDN other than itself. This RDN may be used in order to perform
+ * Returns a constant containing a special RDN which sorts before any
+ * RDN other than itself. This RDN may be used in order to perform
+ * range queries on DN keyed collections such as {@code SortedSet}s and
+ * {@code SortedMap}s. For example, the following code can be used to
+ * construct a range whose contents is a sub-tree of entries, excluding the base entry:
+ *
+ * <pre>
+ * SortedMap<DN, Entry> entries = ...;
+ * DN baseDN = ...;
+ *
+ * // Returns a map containing the baseDN and all of its subordinates.
+ * SortedMap<DN,Entry> subtree = entries.subMap(
+ * baseDN.child(RDN.minValue()), baseDN.child(RDN.maxValue()));
+ * </pre>
+ *
+ * @return A constant containing a special RDN which sorts before any
+ * RDN other than itself.
+ * @see #maxValue()
+ */
+ public static RDN minValue() {
+ return MIN_VALUE;
+ }
+
+ /**
+ * Returns a constant containing a special RDN which sorts after any
+ * RDN other than itself. This RDN may be used in order to perform
* range queries on DN keyed collections such as {@code SortedSet}s and
* {@code SortedMap}s. For example, the following code can be used to
* construct a range whose contents is a sub-tree of entries:
@@ -84,11 +115,12 @@
* DN baseDN = ...;
*
* // Returns a map containing the baseDN and all of its subordinates.
- * SortedMap<DN,Entry> subtree = entries.subMap(baseDN, baseDN.child(RDN.maxValue));
+ * SortedMap<DN,Entry> subtree = entries.subMap(baseDN, baseDN.child(RDN.maxValue()));
* </pre>
*
- * @return A constant containing a special RDN which is greater than any
- * other RDN other than itself.
+ * @return A constant containing a special RDN which sorts after any
+ * RDN other than itself.
+ * @see #minValue()
*/
public static RDN maxValue() {
return MAX_VALUE;
@@ -244,6 +276,9 @@
@Override
public int compareTo(final RDN rdn) {
+ // FIXME how about replacing this method body with the following code?
+ // return toNormalizedByteString().compareTo(rdn.toNormalizedByteString())
+
// Identity.
if (this == rdn) {
return 0;
@@ -253,11 +288,18 @@
if (this == MAX_VALUE) {
return 1;
}
-
if (rdn == MAX_VALUE) {
return -1;
}
+ // MIN_VALUE is always less than any other RDN other than itself.
+ if (this == MIN_VALUE) {
+ return -1;
+ }
+ if (rdn == MIN_VALUE) {
+ return 1;
+ }
+
// Compare number of AVAs first as this is quick and easy.
final int sz1 = avas.length;
final int sz2 = rdn.avas.length;
@@ -348,6 +390,22 @@
}
/**
+ * Indicates whether this RDN includes the specified attribute type.
+ *
+ * @param attributeType The attribute type for which to make the determination.
+ * @return {@code true} if the RDN includes the specified attribute type,
+ * or {@code false} if not.
+ */
+ public boolean hasAttributeType(AttributeType attributeType) {
+ for (AVA ava : avas) {
+ if (ava.getAttributeType().equals(attributeType)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
* Returns an iterator of the AVAs contained in this RDN. The AVAs will be
* returned in the user provided order.
* <p>
@@ -386,7 +444,7 @@
final StringBuilder builder = new StringBuilder();
avas[0].toString(builder);
for (int i = 1; i < avas.length; i++) {
- builder.append('+');
+ builder.append(AVA_CHAR_SEPARATOR);
avas[i].toString(builder);
}
stringValue = builder.toString();
@@ -411,17 +469,22 @@
ByteStringBuilder toNormalizedByteString(final ByteStringBuilder builder) {
switch (size()) {
case 0:
- // Handle RDN.maxValue().
- builder.appendByte(DN.NORMALIZED_AVA_SEPARATOR);
+ if (this == MIN_VALUE) {
+ builder.appendByte(NORMALIZED_RDN_SEPARATOR);
+ } else { // can only be MAX_VALUE
+ builder.appendByte(NORMALIZED_AVA_SEPARATOR);
+ }
break;
case 1:
+ builder.appendByte(NORMALIZED_RDN_SEPARATOR);
getFirstAVA().toNormalizedByteString(builder);
break;
default:
+ builder.appendByte(NORMALIZED_RDN_SEPARATOR);
Iterator<AVA> it = getSortedAvas();
it.next().toNormalizedByteString(builder);
while (it.hasNext()) {
- builder.appendByte(DN.NORMALIZED_AVA_SEPARATOR);
+ builder.appendByte(NORMALIZED_AVA_SEPARATOR);
it.next().toNormalizedByteString(builder);
}
break;
@@ -441,8 +504,13 @@
StringBuilder toNormalizedUrlSafeString(final StringBuilder builder) {
switch (size()) {
case 0:
- // Handle RDN.maxValue().
- builder.append(RDN.AVA_CHAR_SEPARATOR);
+ // since MIN_VALUE and MAX_VALUE are only used for sorting DNs,
+ // it is strange to call toNormalizedUrlSafeString() on one of them
+ if (this == MIN_VALUE) {
+ builder.append(RDN_CHAR_SEPARATOR);
+ } else { // can only be MAX_VALUE
+ builder.append(AVA_CHAR_SEPARATOR);
+ }
break;
case 1:
getFirstAVA().toNormalizedUrlSafe(builder);
@@ -451,7 +519,7 @@
Iterator<AVA> it = getSortedAvas();
it.next().toNormalizedUrlSafe(builder);
while (it.hasNext()) {
- builder.append(RDN.AVA_CHAR_SEPARATOR);
+ builder.append(AVA_CHAR_SEPARATOR);
it.next().toNormalizedUrlSafe(builder);
}
break;
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
index 33c3f71..c497245 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/DNTestCase.java
@@ -12,17 +12,22 @@
* information: "Portions Copyright [year] [name of copyright owner]".
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions copyright 2011-2015 ForgeRock AS.
+ * Portions copyright 2011-2016 ForgeRock AS.
*/
package org.forgerock.opendj.ldap;
import static java.lang.Integer.*;
-import static org.fest.assertions.Assertions.*;
+import static org.assertj.core.api.Assertions.fail;
+import static org.assertj.core.api.Assertions.*;
import static org.testng.Assert.*;
+import java.util.Collection;
import java.util.Iterator;
+import java.util.Map;
import java.util.NoSuchElementException;
+import java.util.TreeMap;
+import java.util.UUID;
import org.forgerock.i18n.LocalizedIllegalArgumentException;
import org.testng.annotations.DataProvider;
@@ -594,18 +599,22 @@
dn.isChildOf((String) null);
}
- /**
- * Tests the parent method that require iteration.
- */
+ /** Tests the parent and rdn method that require iteration. */
@Test
- public void testIterableParent() {
+ public void testIterableParentAndRdn() {
final String str = "ou=people,dc=example,dc=com";
final DN dn = DN.valueOf(str);
// Parent at index 0 is self.
- assertEquals(dn, dn.parent(0));
+ assertEquals(dn.parent(0), dn);
assertEquals(dn.parent(1), DN.valueOf("dc=example,dc=com"));
assertEquals(dn.parent(2), DN.valueOf("dc=com"));
assertEquals(dn.parent(3), DN.rootDN());
+ assertEquals(dn.parent(4), null);
+
+ assertEquals(dn.rdn(0), RDN.valueOf("ou=people"));
+ assertEquals(dn.rdn(1), RDN.valueOf("dc=example"));
+ assertEquals(dn.rdn(2), RDN.valueOf("dc=com"));
+ assertEquals(dn.rdn(3), null);
}
/**
@@ -674,8 +683,6 @@
assertEquals(p.rdn(), RDN.valueOf("dc=bar"));
- assertEquals(p.rdn(), RDN.valueOf("dc=bar"));
-
assertEquals(p.parent(), DN.valueOf("dc=opendj,dc=org"));
assertEquals(p.parent(), e.parent());
@@ -719,15 +726,25 @@
}
/**
- * Tests the root DN.
+ * Tests {@link DN#valueOf(String)}.
*
* @throws Exception
* If the test failed unexpectedly.
*/
@Test(expectedExceptions = { NullPointerException.class, AssertionError.class })
- public void testRootDN2() throws Exception {
- final DN dn = DN.valueOf(null);
- assertEquals(dn, DN.rootDN());
+ public void testValueOfString() throws Exception {
+ DN.valueOf((String) null);
+ }
+
+ /**
+ * Tests {@link DN#valueOf(ByteString)}.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test(expectedExceptions = { NullPointerException.class, AssertionError.class })
+ public void testValueOfByteString() throws Exception {
+ DN.valueOf((ByteString) null);
}
/**
@@ -1030,7 +1047,7 @@
}
@DataProvider
- public Object[][] toIrreversibleNormalizedByteStringDataProvider() {
+ public Object[][] toNormalizedByteStringDataProvider() {
// @formatter:off
return new Object[][] {
// first value to normalize, second value to normalize, expected sign of comparison between the two
@@ -1038,6 +1055,13 @@
{ "dc=example,dc=com", "dc=example,dc=com", 0 },
{ "cn=test+dc=example,dc=com", "cn=test+dc=example,dc=com", 0 },
{ "dc=example+cn=test,dc=com", "cn=test+dc=example,dc=com", 0 },
+ // siblings
+ { "cn=test,dc=com", "cn=test+dc=example,dc=com", -1 },
+ { "cn=test+dc=example,dc=com", "cn=test,dc=com", 1 },
+ { "dc=example,dc=com", "cn=test+dc=example,dc=com", 1 },
+ { "cn=test+dc=example,dc=com", "dc=example,dc=com", -1 },
+ { "dc=example,dc=com", "dc=example+cn=test,dc=com", 1 },
+ { "dc=example+cn=test,dc=com", "dc=example,dc=com", -1 },
// parent entry is followed by its children, not its siblings
{ "dc=com", "dc=example,dc=com", -1 },
{ "dc=com", "dc=test,dc=example,dc=com", -1},
@@ -1071,7 +1095,7 @@
// @formatter:on
}
- @Test(dataProvider = "toIrreversibleNormalizedByteStringDataProvider")
+ @Test(dataProvider = "toNormalizedByteStringDataProvider")
public void testToNormalizedByteString(String first, String second, int expectedCompareResult) {
DN actual = DN.valueOf(first);
DN expected = DN.valueOf(second);
@@ -1092,7 +1116,81 @@
}
@DataProvider
- public Object[][] toIrreversibleReadableStringDataProvider() {
+ private Object[][] minAndMaxRdnsDataProvider() {
+ DN dcCom = DN.valueOf("dc=com");
+ DN dcExampleDcCom = DN.valueOf("dc=example,dc=com");
+ DN cnTestDcCom = DN.valueOf("cn=test,dc=com");
+ return new Object[][] {
+ { dcCom, dcCom.child(RDN.minValue()), -1 },
+ { dcCom, dcCom.child(RDN.maxValue()), -1 },
+ { dcExampleDcCom, dcExampleDcCom.child(RDN.minValue()), -1 },
+ { dcExampleDcCom, dcExampleDcCom.child(RDN.maxValue()), -1 },
+ { dcExampleDcCom, dcCom.child(RDN.minValue()), 1 },
+ { dcExampleDcCom, dcCom.child(RDN.maxValue()), -1 },
+ // siblings
+ { DN.valueOf("cn=test+dc=example,dc=com"), cnTestDcCom.child(RDN.minValue()), 1 },
+ { DN.valueOf("dc=example+cn=test,dc=com"), cnTestDcCom.child(RDN.minValue()), 1 },
+ { DN.valueOf("cn=test+dc=example,dc=com"), cnTestDcCom.child(RDN.maxValue()), 1 },
+ { DN.valueOf("dc=example+cn=test,dc=com"), cnTestDcCom.child(RDN.maxValue()), 1 },
+ };
+ }
+
+ /** Using DN as a Map key depends on this behaviour. In particular MemoryBackend depends on this behaviour. */
+ @Test(dataProvider = "minAndMaxRdnsDataProvider")
+ public void testToNormalizedByteStringWithMinAndMaxRdns(DN dn1, DN dn2, int expectedCompareResult) {
+ int cmp = dn1.toNormalizedByteString().compareTo(dn2.toNormalizedByteString());
+ assertThat(signum(cmp)).isEqualTo(expectedCompareResult);
+ }
+
+ @Test
+ public void testToNormalizedByteStringWithMinAndMaxRdnsInOrderedCollection() {
+ DN dcCom = DN.valueOf("dc=com");
+ DN cnTestDcCom = DN.valueOf("cn=test,dc=com");
+ DN cnDeeperCnTestDcCom = DN.valueOf("cn=deeper,cn=test,dc=com");
+ DN cnTestAndDcExampleDcCom = DN.valueOf("cn=test+dc=example,dc=com");
+ DN dcExampleDcCom = DN.valueOf("dc=example,dc=com");
+
+ TreeMap<ByteString, DN> map = new TreeMap<>();
+ putAll(map, dcCom, cnTestDcCom, cnDeeperCnTestDcCom, cnTestAndDcExampleDcCom, dcExampleDcCom);
+
+ assertThat(subordinates(map, dcCom))
+ .containsExactly(cnTestDcCom, cnDeeperCnTestDcCom, cnTestAndDcExampleDcCom, dcExampleDcCom);
+ assertThat(subordinates(map, cnTestDcCom))
+ .containsExactly(cnDeeperCnTestDcCom);
+
+ assertThat(after(map, cnTestDcCom))
+ .containsExactly(cnDeeperCnTestDcCom, cnTestAndDcExampleDcCom, dcExampleDcCom);
+ assertThat(after(map, cnDeeperCnTestDcCom))
+ .containsExactly(cnTestAndDcExampleDcCom, dcExampleDcCom);
+
+ assertThat(before(map, cnTestDcCom))
+ .containsExactly(dcCom);
+ assertThat(before(map, cnDeeperCnTestDcCom))
+ .containsExactly(dcCom, cnTestDcCom);
+ }
+
+ private void putAll(Map<ByteString, DN> map, DN... dns) {
+ for (DN dn : dns) {
+ map.put(dn.toNormalizedByteString(), dn);
+ }
+ }
+
+ private Collection<DN> subordinates(TreeMap<ByteString, DN> map, DN dn) {
+ return map.subMap(
+ dn.child(RDN.minValue()).toNormalizedByteString(),
+ dn.child(RDN.maxValue()).toNormalizedByteString()).values();
+ }
+
+ private Collection<DN> before(TreeMap<ByteString, DN> map, DN dn) {
+ return map.headMap(dn.toNormalizedByteString(), false).values();
+ }
+
+ private Collection<DN> after(TreeMap<ByteString, DN> map, DN dn) {
+ return map.tailMap(dn.toNormalizedByteString(), false).values();
+ }
+
+ @DataProvider
+ public Object[][] toNormalizedUrlSafeStringDataProvider() {
// @formatter:off
return new Object[][] {
// first value = string used to build DN, second value = expected readable string
@@ -1125,7 +1223,7 @@
// @formatter:on
}
- @Test(dataProvider = "toIrreversibleReadableStringDataProvider")
+ @Test(dataProvider = "toNormalizedUrlSafeStringDataProvider")
public void testToNormalizedUrlSafeString(String dnAsString, String expectedReadableString) {
DN actual = DN.valueOf(dnAsString);
assertEquals(actual.toNormalizedUrlSafeString(), expectedReadableString);
@@ -1141,4 +1239,11 @@
assertEquals(irreversibleReadableString, dn2.toNormalizedUrlSafeString());
assertEquals(irreversibleReadableString, dn3.toNormalizedUrlSafeString());
}
+
+ @Test
+ public void toUUID() {
+ UUID uuid1 = DN.valueOf("dc=example+cn=test,dc=com").toUUID();
+ UUID uuid2 = DN.valueOf("cn=test+dc=example,dc=com").toUUID();
+ assertEquals(uuid1, uuid2);
+ }
}
diff --git a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java
index c893363..5a5a79e 100644
--- a/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java
+++ b/opendj-sdk/opendj-core/src/test/java/org/forgerock/opendj/ldap/schema/DistinguishedNameEqualityMatchingRuleTest.java
@@ -21,6 +21,7 @@
import static org.testng.Assert.assertEquals;
import org.forgerock.opendj.ldap.ByteString;
+import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.ConditionResult;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
@@ -194,8 +195,14 @@
final MatchingRule rule = getRule();
final ByteString normalizedValue1 =
rule.normalizeAttributeValue(ByteString.valueOfUtf8(value1));
- final ByteString expectedValue = ByteString.valueOfUtf8(value2);
+ final ByteString expectedValue = toExpectedNormalizedByteString(value2);
assertEquals(normalizedValue1, expectedValue);
}
+ private ByteString toExpectedNormalizedByteString(final String s) {
+ if (s.isEmpty()) {
+ return ByteString.valueOfUtf8(s);
+ }
+ return new ByteStringBuilder().appendByte(0).appendUtf8(s).toByteString();
+ }
}
--
Gitblit v1.10.0