From 56274dff0f872d02abb69608f6d98fa087c8e6f2 Mon Sep 17 00:00:00 2001
From: neil_a_wilson <neil_a_wilson@localhost>
Date: Thu, 15 Feb 2007 00:21:49 +0000
Subject: [PATCH] Rewrite the DN and RDN code for significant performance improvements, especially in the area of DN parsing.
---
opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java | 21
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java | 4
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java | 373 +--
opendj-sdk/opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java | 45
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java | 9
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java | 7
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java | 495 +--
opendj-sdk/opends/src/server/org/opends/server/types/DN.java | 3167 +++++++++++++++++++++++----
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java | 11
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java | 7
opendj-sdk/opends/src/server/org/opends/server/types/RDN.java | 2507 ++++++++-------------
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java | 7
opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java | 7
opendj-sdk/opends/src/server/org/opends/server/types/AttributeValue.java | 117 +
14 files changed, 4,182 insertions(+), 2,595 deletions(-)
diff --git a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
index 97388e7..b8a5a52 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/backends/jeb/EntryContainer.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.backends.jeb;
@@ -53,6 +53,7 @@
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;
@@ -3100,8 +3101,22 @@
*/
public static DN modDN(DN oldDN, int oldSuffixLen, DN newSuffixDN)
{
- DN localName = oldDN.getLocalName(oldSuffixLen);
- return newSuffixDN.concat(localName);
+ int oldDNNumComponents = oldDN.getNumComponents();
+ int oldDNKeepComponents = oldDNNumComponents - oldSuffixLen;
+ int newSuffixDNComponents = newSuffixDN.getNumComponents();
+
+ RDN[] newDNComponents = new RDN[oldDNKeepComponents+newSuffixDNComponents];
+ for (int i=0; i < oldDNKeepComponents; i++)
+ {
+ newDNComponents[i] = oldDN.getRDN(i);
+ }
+
+ for (int i=oldDNKeepComponents, j=0; j < newSuffixDNComponents; i++,j++)
+ {
+ newDNComponents[i] = newSuffixDN.getRDN(j);
+ }
+
+ return new DN(newDNComponents);
}
/**
diff --git a/opendj-sdk/opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java b/opendj-sdk/opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java
index fa4c9be..18ebd2e 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/tools/makeldif/TemplateEntry.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.tools.makeldif;
@@ -148,29 +148,50 @@
/**
- * Retrieves teh DN for this template entry, if it is known.
+ * Retrieves the DN for this template entry, if it is known.
*
* @return The DN for this template entry if it is known, or
* <CODE>null</CODE> if it cannot yet be determined.
*/
public DN getDN()
{
- if (dn == null) {
- RDN.Builder builder = RDN.createBuilder();
-
- for (AttributeType type : template.getRDNAttributes()) {
- TemplateValue v = getValue(type);
- if (v == null) {
+ if (dn == null)
+ {
+ RDN rdn;
+ AttributeType[] rdnAttrs = template.getRDNAttributes();
+ if (rdnAttrs.length == 1)
+ {
+ AttributeType t = rdnAttrs[0];
+ TemplateValue v = getValue(t);
+ if (v == null)
+ {
return null;
}
- AttributeValue value = new AttributeValue(type,
- v.getValue().toString());
+ AttributeValue value = new AttributeValue(t, v.getValue().toString());
+ rdn = new RDN(t, value);
+ }
+ else
+ {
+ String[] names = new String[rdnAttrs.length];
+ AttributeValue[] values = new AttributeValue[rdnAttrs.length];
+ for (int i=0; i < rdnAttrs.length; i++)
+ {
+ AttributeType t = rdnAttrs[i];
+ TemplateValue v = getValue(t);
+ if (v == null)
+ {
+ return null;
+ }
- builder.append(type, value);
+ names[i] = t.getPrimaryName();
+ values[i] = new AttributeValue(t, v.getValue().toString());
+ }
+
+ rdn = new RDN(rdnAttrs, names, values);
}
- dn = parentDN.concat(builder.getInstance());
+ dn = parentDN.concat(rdn);
}
return dn;
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/AttributeValue.java b/opendj-sdk/opends/src/server/org/opends/server/types/AttributeValue.java
index 9853d0c..e526e99 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/AttributeValue.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/AttributeValue.java
@@ -32,6 +32,7 @@
import org.opends.server.protocols.asn1.ASN1OctetString;
import static org.opends.server.loggers.Debug.*;
+import static org.opends.server.util.StaticUtils.*;
import static org.opends.server.util.Validator.*;
@@ -257,6 +258,122 @@
/**
+ * 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 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.
*
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/DN.java b/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
index 242e0cd..99fdedc 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/DN.java
@@ -22,20 +22,23 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.types;
-import static org.opends.server.loggers.Debug.*;
-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.*;
@@ -43,196 +46,58 @@
* 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 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).
-
+public class DN
+ implements Comparable<DN>, Serializable
+{
/**
* The fully-qualified name of this class for debugging purposes.
*/
private static final String CLASS_NAME =
- "org.opends.server.types.DN";
+ "org.opends.server.types.DN";
+
+
+
+ /**
+ * A singleton instance of the null DN (a DN with no components).
+ */
+ public static DN NULL_DN = new 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 final int numComponents;
- // The index of the first RDN component (furthest from the root) in
- // this DN.
- private final int offset;
+
+ // The number of RDN components that comprise this DN.
+ private int numComponents;
// The set of RDN components that comprise this DN, arranged with
// the suffix as the last element.
- private final RDN[] rdnComponents;
+ private RDN[] rdnComponents;
- // The cached normalized string representation of this DN.
- private final String normalizedDN;
+ // The string representation of this DN.
+ private String dnString;
- // A DN comprising of zero RDN components.
- private static final DN EMPTY_DN = new DN(new RDN[0], 0, 0);
+ // The normalized string representation of this DN.
+ private String normalizedDN;
/**
- * 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.
+ * Creates a new DN with no RDN components (i.e., a null DN or root
+ * DSE).
*/
- 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);
- }
+ public DN()
+ {
+ this(new RDN[0]);
}
@@ -241,203 +106,97 @@
* 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 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.
+ * @param rdnComponents The set of RDN components that make up
+ * this DN.
*/
- private DN(RDN[] rdnComponents, int offset, int count) {
+ public DN(RDN[] rdnComponents)
+ {
assert debugConstructor(CLASS_NAME,
- String.valueOf(rdnComponents), String.valueOf(offset));
+ String.valueOf(rdnComponents));
- this.rdnComponents = rdnComponents;
- this.offset = offset;
- this.numComponents = count;
- this.normalizedDN = normalize();
+ if (rdnComponents == null)
+ {
+ this.rdnComponents = new RDN[0];
+ }
+ else
+ {
+ this.rdnComponents = rdnComponents;
+ }
+
+ numComponents = this.rdnComponents.length;
+ dnString = null;
+ normalizedDN = toNormalizedString();
}
/**
- * 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.
+ * Creates a new DN with the provided set of RDNs, arranged with the
+ * suffix as the last element.
*
- * @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.
+ * @param rdnComponents The set of RDN components that make up
+ * this DN.
*/
- public DN concat(DN localName) {
- ensureNotNull(localName);
+ public DN(ArrayList<RDN> rdnComponents)
+ {
+ assert debugConstructor(CLASS_NAME,
+ String.valueOf(rdnComponents));
- if (localName.isNullDN()) {
- return this;
+ if ((rdnComponents == null) || rdnComponents.isEmpty())
+ {
+ this.rdnComponents = new RDN[0];
+ }
+ else
+ {
+ this.rdnComponents = new RDN[rdnComponents.size()];
+ rdnComponents.toArray(this.rdnComponents);
}
- 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);
+ numComponents = this.rdnComponents.length;
+ dnString = null;
+ normalizedDN = toNormalizedString();
}
/**
- * 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.
+ * Retrieves a singleton instance of the null 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.
+ * @return A singleton instance of the null DN.
*/
- public DN concat(RDN... rdns) {
- ensureNotNull(rdns);
+ public static DN nullDN()
+ {
+ assert debugEnter(CLASS_NAME, "nullDN");
- 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);
+ return NULL_DN;
}
/**
- * Get the parent DN of this DN.
+ * Indicates whether this represents a null DN. This could target
+ * the root DSE for the Directory Server, or the authorization DN
+ * for an anonymous or unauthenticated client.
*
- * @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).
+ * @return <CODE>true</CODE> if this does represent a null DN, or
+ * <CODE>false</CODE> if it does not.
*/
- public DN getParent() {
- if (numComponents <= 1) {
- return null;
- } else {
- return new DN(rdnComponents, offset + 1, numComponents - 1);
- }
+ public boolean isNullDN()
+ {
+ assert debugEnter(CLASS_NAME, "isNullDN");
+
+ return (numComponents == 0);
}
/**
- * Create a local name (a relative DN) from this DN.
- * <p>
- * Examples: <blockquote>
+ * Retrieves the number of RDN components for this DN.
*
- * <pre>
- * DN dn = DN.decode("cn=john,o=example,c=us");
- *
- * dn.getLocalName(0) returns "cn=john,o=example,c=us"
- * dn.getLocalName(1) returns "cn=john,o=example"
- * dn.getLocalName(3) returns "" (null DN).
- * </pre>
- *
- * </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.
+ * @return The number of RDN components for this DN.
*/
- public DN getLocalName(int beginIndex)
- throws IndexOutOfBoundsException {
- return getLocalName(beginIndex, numComponents);
- }
-
-
-
- /**
- * Create a local name (a relative DN) from this DN.
- * <p>
- * Examples: <blockquote>
- *
- * <pre>
- * DN dn = DN.decode("cn=john,o=example,c=us");
- *
- * dn.getLocalName(0, 3) returns "cn=john,o=example,c=us"
- * dn.getLocalName(1, 2) returns "o=example"
- * dn.getLocalName(2, 2) returns "" (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 getLocalName(int beginIndex, int endIndex)
- throws IndexOutOfBoundsException {
- if (beginIndex < 0) {
- throw new IndexOutOfBoundsException("beginIndex out of range: "
- + beginIndex);
- }
-
- if (endIndex > numComponents) {
- throw new IndexOutOfBoundsException("endIndex out of range: "
- + endIndex);
- }
-
- if (beginIndex > endIndex) {
- throw new IndexOutOfBoundsException(
- "beginIndex greater than endIndex");
- }
-
- if (beginIndex == 0 && endIndex == numComponents) {
- return this;
- } else {
- int i = offset + numComponents - endIndex;
- return new DN(rdnComponents, i, endIndex - beginIndex);
- }
- }
-
-
-
- /**
- * Get the number of RDN components that make up this DN.
- *
- * @return Returns the number of RDN components that make up this
- * DN.
- */
- public int getNumComponents() {
+ public int getNumComponents()
+ {
assert debugEnter(CLASS_NAME, "getNumComponents");
return numComponents;
@@ -447,65 +206,89 @@
/**
* Retrieves the outermost RDN component for this DN (i.e., the one
- * that is furthest from the suffix). This method is equivalent to
- * calling <code>getRDN(0)</code> for non-null DNs.
+ * that is furthest from the suffix).
*
- * @return The outermost RDN component for this DN, or
- * <code>null</code> if there are no RDN components in the
- * DN.
+ * @return The outermost RDN component for this DN, or
+ * <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.
+ * Retrieves the RDN component at the specified position in the set
+ * of components for this DN.
*
- * @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.
+ * @param pos The position of the RDN component to retrieve.
+ *
+ * @return The RDN component at the specified position in the set
+ * of components for this DN.
*/
- public RDN getRDN(int index) throws IndexOutOfBoundsException {
- if (index < 0) {
- throw new IndexOutOfBoundsException("index out of range: "
- + index);
- }
+ public RDN getRDN(int pos)
+ {
+ assert debugEnter(CLASS_NAME, "getRDN", String.valueOf(pos));
- if (index >= numComponents) {
- throw new IndexOutOfBoundsException("index out of range: "
- + index);
- }
-
- return rdnComponents[offset + index];
+ return rdnComponents[pos];
}
/**
* Retrieves the DN of the entry that is the immediate parent for
- * this entry.
+ * this entry. Note that this method does not take the server's
+ * naming context configuration into account when making the
+ * determination.
*
- * @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).
+ * @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.
*/
- public DN getParentDNInSuffix() {
- assert debugEnter(CLASS_NAME, "getParentDNInSuffix");
+ public DN getParent()
+ {
+ assert debugEnter(CLASS_NAME, "getParent");
+
+ if (numComponents <= 1)
+ {
+ return null;
+ }
+
+ RDN[] parentComponents = new RDN[numComponents-1];
+ System.arraycopy(rdnComponents, 1, parentComponents, 0,
+ numComponents-1);
+ return new DN(parentComponents);
+ }
+
+
+
+ /**
+ * Retrieves the DN of the entry that is the immediate parent for
+ * this entry. This method does take the server's naming context
+ * configuration into account, so if the current DN is a naming
+ * context for the server, then it will not be considered to have a
+ * parent.
+ *
+ * @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 getParentDNInSuffix()
+ {
+ assert debugEnter(CLASS_NAME, "getParent");
if ((numComponents <= 1) ||
DirectoryServer.isNamingContext(this))
@@ -513,23 +296,92 @@
return null;
}
- return getParent();
+ RDN[] parentComponents = new RDN[numComponents-1];
+ System.arraycopy(rdnComponents, 1, parentComponents, 0,
+ numComponents-1);
+ return new DN(parentComponents);
}
/**
- * Indicates whether this represents a null DN. This could target
- * the root DSE for the Directory Server, or the authorization DN
- * for an anonymous or unauthenticated client.
+ * Creates a new DN that is a child of this DN, using the specified
+ * RDN.
*
- * @return <code>true</code> if this does represent a null DN, or
- * <code>false</code> if it does not.
+ * @param rdn The RDN for the child of this DN.
+ *
+ * @return A new DN that is a child of this DN, using the specified
+ * RDN.
*/
- public boolean isNullDN() {
- assert debugEnter(CLASS_NAME, "isNullDN");
+ public DN concat(RDN rdn)
+ {
+ assert debugEnter(CLASS_NAME, "concat", String.valueOf(rdn));
- return (numComponents == 0);
+ RDN[] newComponents = new RDN[rdnComponents.length+1];
+ newComponents[0] = rdn;
+ System.arraycopy(rdnComponents, 0, newComponents, 1,
+ rdnComponents.length);
+
+ return new DN(newComponents);
+ }
+
+
+
+ /**
+ * Creates a new DN that is a descendant of this DN, using the
+ * specified RDN components.
+ *
+ * @param rdnComponents The RDN components for the descendant of
+ * this DN.
+ *
+ * @return A new DN that is a descendant of this DN, using the
+ * specified RDN components.
+ */
+ public DN concat(RDN[] rdnComponents)
+ {
+ assert debugEnter(CLASS_NAME, "concat",
+ String.valueOf(rdnComponents));
+
+ RDN[] newComponents =
+ new RDN[rdnComponents.length+this.rdnComponents.length];
+ System.arraycopy(rdnComponents, 0, newComponents, 0,
+ rdnComponents.length);
+ System.arraycopy(this.rdnComponents, 0, newComponents,
+ rdnComponents.length, this.rdnComponents.length);
+
+ return new DN(newComponents);
+ }
+
+
+
+ /**
+ * Creates a new DN that is a descendant of this DN, using the
+ * specified DN as a relative base DN. That is, the resulting DN
+ * will first have the components of the provided DN followed by the
+ * components of this DN.
+ *
+ * @param relativeBaseDN The relative base DN to concatenate onto
+ * this DN.
+ *
+ * @return A new DN that is a descendant of this DN, using the
+ * specified DN as a relative base DN.
+ */
+ public DN concat(DN relativeBaseDN)
+ {
+ assert debugEnter(CLASS_NAME, "concat",
+ String.valueOf(relativeBaseDN));
+
+ RDN[] newComponents =
+ new RDN[rdnComponents.length+
+ relativeBaseDN.rdnComponents.length];
+
+ System.arraycopy(relativeBaseDN.rdnComponents, 0, newComponents,
+ 0, relativeBaseDN.rdnComponents.length);
+ System.arraycopy(rdnComponents, 0, newComponents,
+ relativeBaseDN.rdnComponents.length,
+ rdnComponents.length);
+
+ return new DN(newComponents);
}
@@ -539,24 +391,26 @@
* (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));
- ensureNotNull(dn);
-
- int diff = numComponents - dn.numComponents;
- if (diff < 0) {
+ int offset = numComponents - dn.numComponents;
+ if (offset < 0)
+ {
return false;
}
- for (int i = 0; i < dn.numComponents; i++) {
- if (!getRDN(i + diff).equals(dn.getRDN(i))) {
+ for (int i=0; i < dn.numComponents; i++)
+ {
+ if (! rdnComponents[i+offset].equals(dn.rdnComponents[i]))
+ {
return false;
}
}
@@ -571,23 +425,25 @@
* (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));
- ensureNotNull(dn);
-
- int diff = dn.numComponents - numComponents;
- if (diff < 0) {
+ int offset = dn.numComponents - numComponents;
+ if (offset < 0)
+ {
return false;
}
- for (int i = 0; i < numComponents; i++) {
- if (!getRDN(i).equals(dn.getRDN(i + diff))) {
+ for (int i=0; i < numComponents; i++)
+ {
+ if (! rdnComponents[i].equals(dn.rdnComponents[i+offset]))
+ {
return false;
}
}
@@ -598,26 +454,2312 @@
/**
- * Indicates whether the provided object is equal to this DN. In
+ * 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);
+ }
+ }
+
+
+
+ /**
+ * 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;
- } else if (o instanceof DN) {
- DN other = (DN) o;
- return normalizedDN.equals(other.normalizedDN);
- } else {
+ }
+
+ 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);
+
return false;
}
}
@@ -625,12 +2767,13 @@
/**
- * Retrieves the hash code for this DN. The hash code will be the
+ * Retrieves the hash code for this DN. The hash code will be the
* sum of the hash codes for all the RDN components.
*
- * @return The hash code for this DN.
+ * @return The hash code for this DN.
*/
- public int hashCode() {
+ public int hashCode()
+ {
assert debugEnter(CLASS_NAME, "hashCode");
return normalizedDN.hashCode();
@@ -641,14 +2784,34 @@
/**
* Retrieves a string representation of this DN.
*
- * @return A string representation of this DN.
+ * @return A string representation of this DN.
*/
- public String toString() {
+ public String toString()
+ {
assert debugEnter(CLASS_NAME, "toString");
- StringBuilder builder = new StringBuilder();
- toString(builder);
- return builder.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;
}
@@ -657,23 +2820,15 @@
* 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");
+ "java.lang.StringBuilder");
- ensureNotNull(buffer);
-
- if (numComponents != 0) {
- getRDN(0).toString(buffer);
-
- for (int i = 1; i < numComponents; i++) {
- buffer.append(",");
- getRDN(i).toString(buffer);
- }
- }
+ buffer.append(toString());
}
@@ -681,11 +2836,33 @@
/**
* Retrieves a normalized string representation of this DN.
*
- * @return A normalized string representation of this DN.
+ * @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;
}
@@ -695,16 +2872,15 @@
* 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");
+ "java.lang.StringBuilder");
- ensureNotNull(buffer);
-
- buffer.append(normalizedDN);
+ buffer.append(toNormalizedString());
}
@@ -714,69 +2890,54 @@
* 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.
- * @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.
+ * @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(this),
- String.valueOf(dn));
+ public int compareTo(DN dn)
+ {
+ assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(dn));
- 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 {
- return 1;
- }
- } else if (index2 >= 0) {
- return -1;
- } else {
- return 0;
- }
-
- index1--;
- index2--;
+ if (equals(dn))
+ {
+ return 0;
}
- }
-
-
-
- /**
- * 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);
+ else if (isNullDN())
+ {
+ return -1;
+ }
+ else if (dn.isNullDN())
+ {
+ return 1;
+ }
+ else if (isAncestorOf(dn))
+ {
+ 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;
+ }
}
- return buffer.toString();
+ return 0;
}
}
}
+
diff --git a/opendj-sdk/opends/src/server/org/opends/server/types/RDN.java b/opendj-sdk/opends/src/server/org/opends/server/types/RDN.java
index 548f0cf..3bbff0b 100644
--- a/opendj-sdk/opends/src/server/org/opends/server/types/RDN.java
+++ b/opendj-sdk/opends/src/server/org/opends/server/types/RDN.java
@@ -22,29 +22,25 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.types;
-import static org.opends.server.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.TreeMap;
+import java.util.TreeSet;
import org.opends.server.api.OrderingMatchingRule;
import org.opends.server.core.DirectoryServer;
import org.opends.server.protocols.asn1.ASN1OctetString;
-import org.opends.server.util.StaticUtils;
+
+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.*;
@@ -52,55 +48,59 @@
* 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 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.
-
+public class RDN
+ implements Comparable<RDN>
+{
/**
* The fully-qualified name of this class for debugging purposes.
*/
private static final String CLASS_NAME =
- "org.opends.server.types.RDN";
+ "org.opends.server.types.RDN";
+
+
// The set of attribute types for the elements in this RDN.
- private final AttributeType[] attributeTypes;
-
- // The set of user-provided names for the attributes in this RDN.
- private final String[] attributeNames;
+ private AttributeType[] attributeTypes;
// The set of values for the elements in this RDN.
- private final AttributeValue[] attributeValues;
+ private 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.
- */
+ // 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;
+
+ // The set of user-provided names for the attributes in this RDN.
+ private String[] attributeNames;
+
/**
* Creates a new RDN with the provided information.
*
- * @param type
- * The attribute type for this RDN.
- * @param value
- * The value for this RDN.
- * @return Returns the new RDN.
+ * @param attributeType The attribute type for this RDN. It must
+ * not be {@code null}.
+ * @param attributeValue The value for this RDN. It must not be
+ * {@code null}.
*/
- public static RDN create(AttributeType type, AttributeValue value) {
- return create(type, type.getNameOrOID(), value);
+ 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;
}
@@ -108,392 +108,98 @@
/**
* Creates a new RDN with the provided information.
*
- * @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.
+ * @param attributeType The attribute type for this RDN. It must
+ * not be {@code null}.
+ * @param attributeName The user-provided name for this RDN. It
+ * must not be {@code null}.
+ * @param attributeValue The value for this RDN. It must not be
+ * {@code null}.
*/
- public static RDN create(AttributeType type, String name,
- AttributeValue value) {
- ensureNotNull(type, name, value);
+ public RDN(AttributeType attributeType, String attributeName,
+ AttributeValue attributeValue)
+ {
+ assert debugConstructor(CLASS_NAME, String.valueOf(attributeType),
+ String.valueOf(attributeName),
+ String.valueOf(attributeValue));
- AttributeType[] types = new AttributeType[] { type };
- String[] names = new String[] { name };
- AttributeValue[] values = new AttributeValue[] { value };
+ attributeTypes = new AttributeType[] { attributeType };
+ attributeNames = new String[] { attributeName };
+ attributeValues = new AttributeValue[] { attributeValue };
- return new RDN(types, names, values);
+ numValues = 1;
+ rdnString = null;
+ normalizedRDN = null;
}
/**
- * Create a new RDN builder which can be used to incrementally build
- * a new RDN.
+ * Creates a new RDN with the provided information. The number of
+ * type, name, and value elements must be nonzero and equal.
*
- * @return Returns the new RDN builder.
+ * @param attributeTypes The set of attribute types for this RDN.
+ * It must not be empty or {@code null}.
+ * @param attributeNames The set of user-provided names for this
+ * RDN. It must have the same number of
+ * elements as the {@attributeTypes}
+ * argument.
+ * @param attributeValues The set of values for this RDN. It must
+ * have the same number of elements as the
+ * {@attributeTypes} argument.
*/
- public static Builder createBuilder() {
- return new 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;
}
/**
- * 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.
+ * 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.
+ * It must not be empty or {@code null}.
+ * @param attributeNames The set of user-provided names for this
+ * RDN. It must have the same number of
+ * elements as the {@attributeTypes}
+ * argument.
+ * @param attributeValues The set of values for this RDN. It must
+ * have the same number of elements as the
+ * {@attributeTypes} argument.
*/
- public static final class Builder {
- // The list of attribute types.
- private List<AttributeType> attributeTypes;
+ public RDN(AttributeType[] attributeTypes, String[] attributeNames,
+ AttributeValue[] attributeValues)
+ {
+ assert debugConstructor(CLASS_NAME,
+ String.valueOf(attributeTypes),
+ String.valueOf(attributeNames),
+ String.valueOf(attributeValues));
- // The list of user-provided attribute names.
- private List<String> attributeNames;
+ this.numValues = attributeTypes.length;
+ this.attributeTypes = attributeTypes;
+ this.attributeNames = attributeNames;
+ this.attributeValues = attributeValues;
- // 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);
- }
+ rdnString = null;
+ normalizedRDN = null;
}
@@ -501,21 +207,21 @@
/**
* 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.
+ * @param attributeType The attribute type for this RDN. It must
+ * not be {@code null}.
+ * @param attributeValue The value for this RDN. It must not be
+ * {@code null}.
+ *
+ * @return The RDN created with the provided information.
*/
- private RDN(AttributeType[] types, String[] names,
- AttributeValue[] values) {
- assert debugConstructor(CLASS_NAME, String.valueOf(types), String
- .valueOf(names), String.valueOf(values));
+ public static RDN create(AttributeType attributeType,
+ AttributeValue attributeValue)
+ {
+ assert debugEnter(CLASS_NAME, "create",
+ String.valueOf(attributeType),
+ String.valueOf(attributeValue));
- this.attributeTypes = types;
- this.attributeNames = names;
- this.attributeValues = values;
+ return new RDN(attributeType, attributeValue);
}
@@ -524,89 +230,14 @@
* Retrieves the number of attribute-value pairs contained in this
* RDN.
*
- * @return The number of attribute-value pairs contained in this
- * RDN.
+ * @return The number of attribute-value pairs contained in this
+ * RDN.
*/
- public int getNumValues() {
+ public int getNumValues()
+ {
assert debugEnter(CLASS_NAME, "getNumValues");
- return attributeTypes.length;
- }
-
-
-
- /**
- * Retrieves the attribute type at the specified AVA in 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 getAttributeType(int index)
- throws IndexOutOfBoundsException {
- assert debugEnter(CLASS_NAME, "getAttributeType");
-
- 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];
+ return numValues;
}
@@ -614,19 +245,21 @@
/**
* 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));
- ensureNotNull(attributeType);
-
- for (AttributeType t : attributeTypes) {
- if (t.equals(attributeType)) {
+ for (AttributeType t : attributeTypes)
+ {
+ if (t.equals(attributeType))
+ {
return true;
}
}
@@ -637,28 +270,100 @@
/**
+ * 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 attribute type at the specified position in the set
+ * of attribute types for this RDN.
+ *
+ * @param pos The position of the attribute type to retrieve.
+ *
+ * @return The attribute type at the specified position in the set
+ * of attribute types for this RDN.
+ */
+ public AttributeType getAttributeType(int pos)
+ {
+ assert debugEnter(CLASS_NAME, "getAttributeType",
+ String.valueOf(pos));
+
+ return attributeTypes[pos];
+ }
+
+
+
+ /**
+ * Retrieves the name for the attribute type at the specified
+ * position in the set of attribute types for this RDN.
+ *
+ * @param pos The position of the attribute type for which to
+ * retrieve the name.
+ *
+ * @return The name for the attribute type at the specified
+ * position in the set of attribute types for this RDN.
+ */
+ public String getAttributeName(int pos)
+ {
+ assert debugEnter(CLASS_NAME, "getAttributeName",
+ String.valueOf(pos));
+
+ return attributeNames[pos];
+ }
+
+
+
+ /**
* 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.
- * @return The value for the requested attribute type, or
- * <code>null</code> if the specified attribute type is
- * not present in the RDN.
+ * @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.
*/
- 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));
- ensureNotNull(attributeType);
-
- for (int i = 0; i < attributeTypes.length; i++) {
- if (attributeTypes[i].equals(attributeType)) {
+ for (int i=0; i < numValues; i++)
+ {
+ if (attributeTypes[i].equals(attributeType))
+ {
return attributeValues[i];
}
}
@@ -669,59 +374,121 @@
/**
+ * Retrieves the value for the attribute type at the specified
+ * position in the set of attribute types for this RDN.
+ *
+ * @param pos The position of the attribute type for which to
+ * retrieve the value.
+ *
+ * @return The value for the attribute type at the specified
+ * position in the set of attribute types for this RDN.
+ */
+ public AttributeValue getAttributeValue(int pos)
+ {
+ assert debugEnter(CLASS_NAME, "getAttributeName",
+ String.valueOf(pos));
+
+ return attributeValues[pos];
+ }
+
+
+
+ /**
* 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 (attributeTypes.length > 1);
+ return (numValues > 1);
}
/**
- * 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)}.
+ * Indicates whether this RDN contains the specified type-value
+ * pair.
*
- * @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.
+ * @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.
*/
- public static RDN valueOf(String s) throws DirectoryException {
- return decode(s);
+ 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;
}
/**
- * Decodes the provided ASN.1 octet string as a RDN.
+ * Adds the provided type-value pair from this RDN. Note that this
+ * is intended only for internal use when constructing DN values.
*
- * @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.
+ * @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).
*/
- public static RDN decode(ByteString rdnString)
- throws DirectoryException {
- assert debugEnter(CLASS_NAME, "decode",
- String.valueOf(rdnString));
+ boolean addValue(AttributeType type, String name,
+ AttributeValue value)
+ {
+ assert debugEnter(CLASS_NAME, "addValue", String.valueOf(type),
+ String.valueOf(name), String.valueOf(value));
- ensureNotNull(rdnString);
+ for (int i=0; i < numValues; i++)
+ {
+ if (attributeTypes[i].equals(type) &&
+ attributeValues[i].equals(value))
+ {
+ return false;
+ }
+ }
- // Use string-based decoder.
- return decode(rdnString.stringValue());
+ 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;
}
@@ -729,75 +496,452 @@
/**
* Decodes the provided string 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.
+ * @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.
*/
public static RDN decode(String rdnString)
- throws DirectoryException {
+ throws DirectoryException
+ {
assert debugEnter(CLASS_NAME, "decode",
- String.valueOf(rdnString));
+ String.valueOf(rdnString));
- ensureNotNull(rdnString);
- // Use an RDN builder to parse the string.
- Builder builder = createBuilder();
- int pos = builder.parse(rdnString, 0, false);
+ // 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);
+ }
- // Make sure that the string did not contain any trailing RDNs.
- if (pos != -1) {
- int msgID = MSGID_RDN_UNEXPECTED_COMMA;
+ 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 == ';'))
+ {
+ int msgID = MSGID_RDN_UNEXPECTED_COMMA;
String message = getMessage(msgID, rdnString, pos);
throw new DirectoryException(ResultCode.INVALID_DN_SYNTAX,
- message, msgID);
+ message, msgID);
}
- // Return the parsed RDN instance.
- return builder.getInstance();
+
+ // 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);
+ }
+ }
}
/**
- * Indicates whether the provided object is equal to this RDN. It
- * 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).
+ * Creates a duplicate of this RDN that can be modified without
+ * impacting this RDN.
*
- * @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.
+ * @return A duplicate of this RDN that can be modified without
+ * impacting this RDN.
*/
- public boolean equals(Object o) {
+ 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);
+ }
+
+
+
+ /**
+ * 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.
+ *
+ * @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)
+ {
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;
+ }
- String nvalue1 = toNormalizedString();
- String nvalue2 = other.toNormalizedString();
- return nvalue1.equals(nvalue2);
- } else {
+ if ((o == null) || (! (o instanceof RDN)))
+ {
return false;
}
+
+ RDN rdn = (RDN) o;
+ return toNormalizedString().equals(rdn.toNormalizedString());
}
/**
- * Retrieves the hash code for this RDN. It will be calculated as
- * the hash code of the RDN's normalized string representation.
+ * Retrieves the hash code for this RDN. It will be calculated as
+ * the sum of the hash codes of the types and values.
*
- * @return The hash code for this RDN.
+ * @return The hash code for this RDN.
*/
- public int hashCode() {
+ public int hashCode()
+ {
assert debugEnter(CLASS_NAME, "hashCode");
return toNormalizedString().hashCode();
@@ -808,14 +952,30 @@
/**
* Retrieves a string representation of this RDN.
*
- * @return A string representation of this RDN.
+ * @return A string representation of this RDN.
*/
- public String toString() {
- assert debugEnter(CLASS_NAME, "toString");
+ public String toString()
+ {
+ if (rdnString == null)
+ {
+ StringBuilder buffer = new StringBuilder();
- StringBuilder buffer = new StringBuilder();
- toString(buffer);
- return buffer.toString();
+ 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;
}
@@ -824,29 +984,15 @@
* 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");
+ "java.lang.StringBuilder");
- 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);
- }
+ buffer.append(toString());
}
@@ -854,38 +1000,14 @@
/**
* Retrieves a normalized string representation of this RDN.
*
- * @return A normalized string representation of this RDN.
+ * @return A normalized string representation of this RDN.
*/
- 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();
+ public String toNormalizedString()
+ {
+ if (normalizedRDN == null)
+ {
+ StringBuilder buffer = new StringBuilder();
+ toNormalizedString(buffer);
}
return normalizedRDN;
@@ -897,899 +1019,262 @@
* 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");
+ "java.lang.StringBuilder");
- ensureNotNull(buffer);
-
- buffer.append(toNormalizedString());
- }
-
-
-
- /**
- * 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));
-
- ensureNotNull(rdn);
-
- // Handle the common case efficiently.
- if (attributeTypes.length == 1
- && rdn.attributeTypes.length == 1) {
- AttributeType type1 = attributeTypes[0];
- AttributeType type2 = rdn.attributeTypes[0];
-
- AttributeValue value1 = attributeValues[0];
- AttributeValue value2 = rdn.attributeValues[0];
-
- return compareAVA(type1, value1, type2, value2);
- }
-
- // 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);
- }
-
- for (int i = 0; i < rdn.attributeTypes.length; i++) {
- map2.put(rdn.attributeTypes[i].getNameOrOID(), i);
- }
-
- // 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;
- }
- }
-
- // 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;
- }
- }
-
-
-
- /**
- * Compare two AVAs for order.
- *
- * @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.
- */
- private int compareAVA(AttributeType type1, AttributeValue value1,
- AttributeType type2, AttributeValue value2) {
- if (type1.equals(type2)) {
- OrderingMatchingRule rule = type1.getOrderingMatchingRule();
-
- 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);
-
- // Just get the raw values and do a comparison between them.
- byte[] b1 = value1.getValue().value();
- byte[] b2 = value2.getValue().value();
-
- return StaticUtils.compare(b1, b2);
- }
- } else {
- String name1 = toLowerCase(type1.getNameOrOID());
- String name2 = toLowerCase(type2.getNameOrOID());
-
- return name1.compareTo(name2);
- }
- }
-
-
-
- /**
- * 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());
- }
- }
-
-
-
- /**
- * 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) {
+ if (normalizedRDN != null)
+ {
+ buffer.append(normalizedRDN);
return;
}
- // Assume 1-byte UTF8 and that no quoting will be required.
- buffer.ensureCapacity(buffer.length() + length);
+ boolean bufferEmpty = (buffer.length() == 0);
- // Quote leading space or #.
- char c = value.charAt(0);
- if (c == ' ' || c == '#') {
- buffer.append('\\');
- buffer.append(c);
- } else {
- quoteChar(buffer, c);
- }
+ if (attributeNames.length == 1)
+ {
+ toLowerCase(attributeTypes[0].getNameOrOID(), buffer);
+ buffer.append('=');
- // 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);
+ try
+ {
+ buffer.append(
+ attributeValues[0].getNormalizedDNStringValue());
}
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "toNormalizedString", e);
+
+ buffer.append(attributeValues[0].getStringValue());
+ }
+ }
+ else
+ {
+ TreeSet<String> rdnElementStrings = new TreeSet<String>();
+
+ for (int i=0; i < attributeNames.length; i++)
+ {
+ StringBuilder b2 = new StringBuilder();
+ toLowerCase(attributeTypes[i].getNameOrOID(), b2);
+ b2.append('=');
+
+ try
+ {
+ b2.append(attributeValues[i].getNormalizedStringValue());
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "toNormalizedString", e);
+
+ b2.append(attributeValues[i].getStringValue());
+ }
+
+ rdnElementStrings.add(b2.toString());
+ }
+
+ Iterator<String> iterator = rdnElementStrings.iterator();
+ buffer.append(iterator.next());
+
+ while (iterator.hasNext())
+ {
+ buffer.append('+');
+ buffer.append(iterator.next());
+ }
+ }
+
+ if (bufferEmpty)
+ {
+ normalizedRDN = buffer.toString();
}
}
/**
- * Encode a single attribute value from an RDN according to the DN
- * string encoding rules.
+ * Compares this RDN with the provided RDN based on an alphabetic
+ * comparison of the attribute names and values.
*
- * @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 rdn The RDN against which to compare this RDN.
*
- * @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.
+ * @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.
*/
- 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();
+ public int compareTo(RDN rdn)
+ {
+ assert debugEnter(CLASS_NAME, "compareTo", String.valueOf(rdn));
- int length = dnString.length();
+ if ((attributeTypes.length == 1) &&
+ (rdn.attributeTypes.length == 1))
+ {
+ if (attributeTypes[0].equals(rdn.attributeTypes[0]))
+ {
+ int valueComparison;
+ OrderingMatchingRule omr =
+ attributeTypes[0].getOrderingMatchingRule();
+ if (omr == null)
+ {
+ try
+ {
+ return attributeValues[0].getNormalizedStringValue().
+ compareTo(rdn.attributeValues[0].
+ getNormalizedStringValue());
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "compareTo", e);
- // 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);
+ return attributeValues[0].getStringValue().
+ compareTo(rdn.attributeValues[0].
+ getStringValue());
+ }
}
- }
- }
+ else
+ {
+ try
+ {
+ return omr.compareValues(
+ attributeValues[0].getNormalizedValue(),
+ rdn.attributeValues[0].getNormalizedValue());
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "compareTo", e);
- // 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;
+ return omr.compareValues(
+ attributeValues[0].getValue(),
+ rdn.attributeValues[0].getValue());
}
}
}
-
- 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;
- }
+ else
+ {
+ String name1 = toLowerCase(attributeTypes[0].getNameOrOID());
+ String name2 =
+ toLowerCase(rdn.attributeTypes[0].getNameOrOID());
+ return name1.compareTo(name2);
}
-
- 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;
+ if (equals(rdn))
+ {
+ return 0;
}
- // 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);
- }
+ TreeMap<String,AttributeType> typeMap1 =
+ new TreeMap<String,AttributeType>();
+ TreeMap<String,AttributeValue> valueMap1 =
+ new TreeMap<String,AttributeValue>();
+ for (int i=0; i < attributeTypes.length; i++)
+ {
+ String lowerName =
+ toLowerCase(attributeTypes[i].getNameOrOID());
+ typeMap1.put(lowerName, attributeTypes[i]);
+ valueMap1.put(lowerName, attributeValues[i]);
+ }
- 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);
+ TreeMap<String,AttributeType> typeMap2 =
+ new TreeMap<String,AttributeType>();
+ TreeMap<String,AttributeValue> valueMap2 =
+ new TreeMap<String,AttributeValue>();
+ for (int i=0; i < rdn.attributeTypes.length; i++)
+ {
+ String lowerName =
+ toLowerCase(rdn.attributeTypes[i].getNameOrOID());
+ typeMap2.put(lowerName, rdn.attributeTypes[i]);
+ valueMap2.put(lowerName, rdn.attributeValues[i]);
+ }
+
+ Iterator<String> iterator1 = valueMap1.keySet().iterator();
+ Iterator<String> iterator2 = valueMap2.keySet().iterator();
+ String name1 = iterator1.next();
+ String name2 = iterator2.next();
+ AttributeType type1 = typeMap1.get(name1);
+ AttributeType type2 = typeMap2.get(name2);
+ AttributeValue value1 = valueMap1.get(name1);
+ AttributeValue value2 = valueMap2.get(name2);
+
+ while (true)
+ {
+ if (type1.equals(type2))
+ {
+ int valueComparison;
+ OrderingMatchingRule omr = type1.getOrderingMatchingRule();
+ if (omr == null)
+ {
+ try
+ {
+ valueComparison =
+ value1.getNormalizedStringValue().compareTo(
+ value2.getNormalizedStringValue());
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "compareTo", e);
+
+ valueComparison =
+ value1.getStringValue().compareTo(
+ value2.getStringValue());
+ }
}
- }
+ else
+ {
+ try
+ {
+ valueComparison =
+ omr.compareValues(value1.getNormalizedValue(),
+ value2.getNormalizedValue());
+ }
+ catch (Exception e)
+ {
+ assert debugException(CLASS_NAME, "compareTo", e);
- // 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);
+ valueComparison =
+ omr.compareValues(value1.getValue(),
+ value2.getValue());
+ }
+ }
- 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);
+ if (valueComparison == 0)
+ {
+ if (! iterator1.hasNext())
+ {
+ if (iterator2.hasNext())
+ {
+ return -1;
}
- } 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
+ {
+ return 0;
}
- } 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;
+ if (! iterator2.hasNext())
+ {
+ return 1;
}
+
+ name1 = iterator1.next();
+ name2 = iterator2.next();
+ type1 = typeMap1.get(name1);
+ type2 = typeMap2.get(name2);
+ value1 = valueMap1.get(name1);
+ value2 = valueMap2.get(name2);
+ }
+ else
+ {
+ return valueComparison;
}
}
-
- attributeValue.setValue(valueString.toString());
- return pos;
+ else
+ {
+ return name1.compareTo(name2);
+ }
}
}
-
-
-
- /**
- * 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);
- }
- }
-
}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
index cf882a8..8549eb3 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/backends/jeb/TestBackendImpl.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.backends.jeb;
@@ -1278,4 +1278,4 @@
assertTrue(debugString.contains("NOT-INDEXED"));
}
-}
\ No newline at end of file
+}
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java
index e4f0458..d2ca0fb 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/monitors/InternalSearchMonitorTestCase.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.monitors;
@@ -140,10 +140,11 @@
throws Exception
{
AttributeType cnType = DirectoryServer.getAttributeType(ATTR_COMMON_NAME);
-
- RDN rdn0 = RDN.create(cnType, new AttributeValue(cnType, monitorName));
- RDN rdn1 = RDN.create(cnType, new AttributeValue(cnType, "monitor"));
- DN monitorDN = DN.create(rdn0, rdn1);
+
+ RDN[] rdns = new RDN[2];
+ rdns[0] = RDN.create(cnType, new AttributeValue(cnType, monitorName));
+ rdns[1] = RDN.create(cnType, new AttributeValue(cnType, "monitor"));
+ DN monitorDN = new DN(rdns);
InternalClientConnection conn =
InternalClientConnection.getRootConnection();
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java
index e66a856..77403da 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestAddResponseProtocolOp.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
@@ -82,9 +82,10 @@
AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
- RDN rdn = RDN.create(attribute, attributeValue);
-
- dn = DN.create(rdn);
+ RDN[] rdns = new RDN[1];
+ rdns[0] = RDN.create(attribute, attributeValue);
+
+ dn = new DN(rdns);
}
/**
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java
index 79a1d3e..00bbc83 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestCompareResponseProtocolOp.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
@@ -84,8 +84,9 @@
AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
- RDN rdn = RDN.create(attribute, attributeValue);
- dn = DN.create(rdn);
+ RDN[] rdns = new RDN[1];
+ rdns[0] = RDN.create(attribute, attributeValue);
+ dn = new DN(rdns);
}
/**
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java
index 7988f13..d02563d 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestDeleteResponseProtocolOp.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
@@ -82,8 +82,9 @@
AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
- RDN rdn = RDN.create(attribute, attributeValue);
- dn = DN.create(rdn);
+ RDN[] rdns = new RDN[1];
+ rdns[0] = RDN.create(attribute, attributeValue);
+ dn = new DN(rdns);
}
/**
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java
index dfed6c8..377f716 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyDNResponseProtocolOp.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
@@ -84,8 +84,9 @@
AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
- RDN rdn = RDN.create(attribute, attributeValue);
- dn = DN.create(rdn);
+ RDN[] rdns = new RDN[1];
+ rdns[0] = RDN.create(attribute, attributeValue);
+ dn = new DN(rdns);
}
/**
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java
index e5e67cf..2c72c33 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/protocols/ldap/TestModifyResponseProtocolOp.java
@@ -22,7 +22,7 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.protocols.ldap;
@@ -84,8 +84,9 @@
AttributeValue attributeValue = new AttributeValue(attribute, "testValue");
- RDN rdn = RDN.create(attribute, attributeValue);
- dn = DN.create(rdn);
+ RDN[] rdns = new RDN[1];
+ rdns[0] = RDN.create(attribute, attributeValue);
+ dn = new DN(rdns);
}
/**
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
index 34bca38..c05b75e 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestDN.java
@@ -22,12 +22,14 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.types;
+import java.util.ArrayList;
+
import static org.testng.Assert.*;
import org.opends.server.TestCaseUtils;
@@ -54,7 +56,12 @@
return new Object[][] {
{ "", "", "" },
{ " ", "", "" },
+ { "cn=", "cn=", "cn=" },
+ { "cn= ", "cn=", "cn=" },
+ { "cn =", "cn=", "cn=" },
+ { "cn = ", "cn=", "cn=" },
{ "dc=com", "dc=com", "dc=com" },
+ { "dc=com+o=com", "dc=com+o=com", "dc=com+o=com" },
{ "DC=COM", "dc=com", "DC=COM" },
{ "dc = com", "dc=com", "dc=com" },
{ " dc = com ", "dc=com", "dc=com" },
@@ -126,12 +133,17 @@
*/
@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 " }, { "=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" } };
}
@@ -162,23 +174,9 @@
* @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]);
+ DN dn = new DN(new RDN[0]);
assertEquals(dn, DN.nullDN());
}
@@ -193,7 +191,37 @@
*/
@Test
public void testCreateNullDN2() throws Exception {
- DN dn = DN.create();
+ DN dn = new DN();
+
+ assertEquals(dn, DN.nullDN());
+ }
+
+
+
+ /**
+ * Tests the create method.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testCreateNullDN3() throws Exception {
+ DN dn = new DN((RDN[]) null);
+
+ assertEquals(dn, DN.nullDN());
+ }
+
+
+
+ /**
+ * Tests the create method.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testCreateNullDN4() throws Exception {
+ DN dn = new DN((ArrayList<RDN>) null);
assertEquals(dn, DN.nullDN());
}
@@ -208,22 +236,7 @@
*/
@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"));
+ DN dn = new DN(new RDN[] { RDN.decode("dc=com") });
assertEquals(dn, DN.decode("dc=com"));
}
@@ -238,7 +251,7 @@
*/
@Test
public void testCreateWithMultipleRDNs1() throws Exception {
- DN dn = DN.create(new RDN[] { RDN.decode("dc=foo"),
+ DN dn = new DN(new RDN[] { RDN.decode("dc=foo"),
RDN.decode("dc=opends"), RDN.decode("dc=org") });
assertEquals(dn, DN.decode("dc=foo,dc=opends,dc=org"));
@@ -254,8 +267,11 @@
*/
@Test
public void testCreateWithMultipleRDNs2() throws Exception {
- DN dn = DN.create(RDN.decode("dc=foo"), RDN.decode("dc=opends"),
- RDN.decode("dc=org"));
+ ArrayList<RDN> rdnList = new ArrayList<RDN>();
+ rdnList.add(RDN.decode("dc=foo"));
+ rdnList.add(RDN.decode("dc=opends"));
+ rdnList.add(RDN.decode("dc=org"));
+ DN dn = new DN(rdnList);
assertEquals(dn, DN.decode("dc=foo,dc=opends,dc=org"));
}
@@ -309,23 +325,32 @@
/**
- * Tests the <CODE>valueOf</CODE> method which takes a String
- * argument.
+ * Tests the toNoramlizedString methods.
*
- * @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);
+ public void testToNormalizedString() throws Exception {
+ DN dn = DN.decode("dc=example,dc=com");
+
+ StringBuilder buffer = new StringBuilder();
+ dn.toNormalizedString(buffer);
+ assertEquals(buffer.toString(), "dc=example,dc=com");
+
+ assertEquals(dn.toNormalizedString(), "dc=example,dc=com");
+ }
+
+
+
+ /**
+ * Tests both variants of the {@code decode} method with null arguments.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ public void testDecodeNull() throws Exception {
+ assertEquals(DN.decode((ByteString) null), DN.nullDN());
+ assertEquals(DN.decode((String) null), DN.nullDN());
}
@@ -386,32 +411,6 @@
/**
- * 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 = "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");
- }
-
-
-
- /**
* Test the nullDN method.
*
* @throws Exception
@@ -530,6 +529,50 @@
/**
+ * Retrieves the naming contexts defined in the server.
+ */
+ @DataProvider(name = "namingContexts")
+ public Object[][] getNamingContexts() {
+ ArrayList<DN> contextList = new ArrayList<DN>();
+ for (DN baseDN : DirectoryServer.getPublicNamingContexts().keySet())
+ {
+ contextList.add(baseDN);
+ }
+
+ for (DN baseDN : DirectoryServer.getPrivateNamingContexts().keySet())
+ {
+ contextList.add(baseDN);
+ }
+
+ Object[][] contextArray = new Object[contextList.size()][1];
+ for (int i=0; i < contextArray.length; i++)
+ {
+ contextArray[i][0] = contextList.get(i);
+ }
+
+ return contextArray;
+ }
+
+
+
+ /**
+ * Tests the getParentDNInSuffix method.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test(dataProvider = "namingContexts")
+ public void testGetParentDNInSuffix(DN namingContext) throws Exception {
+ assertNull(namingContext.getParentDNInSuffix());
+
+ DN childDN = namingContext.concat(RDN.decode("ou=People"));
+ assertNotNull(childDN.getParentDNInSuffix());
+ assertEquals(childDN.getParentDNInSuffix(), namingContext);
+ }
+
+
+
+ /**
* Test the getParent method's interaction with other methods.
*
* @throws Exception
@@ -574,9 +617,6 @@
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"));
}
@@ -791,10 +831,6 @@
.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"));
}
@@ -843,21 +879,6 @@
* @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 {
@@ -898,52 +919,6 @@
/**
- * 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);
- }
-
-
-
- /**
* Get local name test data provider.
*
* @return The array of test data.
@@ -986,86 +961,6 @@
/**
- * 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 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);
-
- assertFalse(l.isNullDN());
-
- assertEquals(l.getNumComponents(), 2);
-
- assertEquals(l.compareTo(e), 0);
- assertEquals(e.compareTo(l), 0);
-
- assertTrue(l.isAncestorOf(DN.decode("dc=foo,dc=bar,dc=opends")));
- assertFalse(DN.decode("dc=foo,dc=bar,dc=opends").isAncestorOf(l));
-
- 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.
@@ -1244,7 +1139,8 @@
{ "dc=aaa,dc=ccc", "dc=bbb", 1 },
{ "dc=bbb,dc=ccc", "dc=bbb", 1 },
{ "dc=ccc,dc=ccc", "dc=bbb", 1 },
-
+ { "", "dc=bbb", -1 },
+ { "dc=bbb", "", 1 }
};
}
@@ -1280,6 +1176,21 @@
/**
+ * Tests the equals method with a value that's not a DN.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testEqualsNonDN() throws Exception {
+ DN dn = DN.decode("dc=example,dc=com");
+
+ assertFalse(dn.equals("not a DN"));
+ }
+
+
+
+ /**
* Test DN hashCode
*
* @param first
@@ -1366,5 +1277,5 @@
DN dn = DN.decode(rawDN);
assertEquals(dn.toString(), stringDN);
}
-
}
+
diff --git a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
index eca7aa1..2df2dc9 100644
--- a/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
+++ b/opendj-sdk/opends/tests/unit-tests-testng/src/server/org/opends/server/types/TestRDN.java
@@ -22,12 +22,14 @@
* CDDL HEADER END
*
*
- * Portions Copyright 2006 Sun Microsystems, Inc.
+ * Portions Copyright 2006-2007 Sun Microsystems, Inc.
*/
package org.opends.server.types;
+import java.util.ArrayList;
+
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
@@ -93,113 +95,6 @@
- // 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.
*
@@ -208,7 +103,7 @@
*/
@Test
public void testConstructor() throws Exception {
- RDN rdn = RDN.create(AT_DC, AV_DC_ORG);
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
assertEquals(rdn.getNumValues(), 1);
assertEquals(rdn.getAttributeType(0), AT_DC);
@@ -226,7 +121,7 @@
*/
@Test
public void testConstructorWithName() throws Exception {
- RDN rdn = RDN.create(AT_DC, "domainComponent", AV_DC_ORG);
+ RDN rdn = new RDN(AT_DC, "domainComponent", AV_DC_ORG);
assertEquals(rdn.getNumValues(), 1);
assertEquals(rdn.getAttributeType(0), AT_DC);
@@ -237,103 +132,54 @@
/**
- * Test RDN builder.
+ * Test RDN construction with a multiple AVA elements.
*
* @throws Exception
* If the test failed unexpectedly.
*/
@Test
- public void testBuilder() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
+ public void testConstructorMultiAVA() throws Exception {
+ AttributeType[] attrTypes = { AT_DC, AT_CN };
+ String[] attrNames = { AT_DC.getNameOrOID(),
+ AT_CN.getNameOrOID() };
+ AttributeValue[] attrValues = { AV_DC_ORG, AV_CN };
- builder.append(AT_DC, AV_DC_ORG);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(attrTypes, attrNames, attrValues);
- assertEquals(rdn.getNumValues(), 1);
+ 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);
}
/**
- * Test RDN builder.
+ * Test RDN construction with a multiple AVA elements.
*
* @throws Exception
* If the test failed unexpectedly.
*/
@Test
- public void testBuilderWithName() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
+ public void testConstructorMultiAVAList() throws Exception {
+ ArrayList<AttributeType> typeList = new ArrayList<AttributeType>();
+ ArrayList<String> nameList = new ArrayList<String>();
+ ArrayList<AttributeValue> valueList = new ArrayList<AttributeValue>();
- builder.append(AT_DC, "domainComponent", AV_DC_ORG);
- RDN rdn = builder.getInstance();
+ typeList.add(AT_DC);
+ nameList.add(AT_DC.getNameOrOID());
+ valueList.add(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);
- }
+ typeList.add(AT_CN);
+ nameList.add(AT_CN.getNameOrOID());
+ valueList.add(AV_CN);
-
-
- /**
- * 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();
+ RDN rdn = new RDN(typeList, nameList, valueList);
assertEquals(rdn.getNumValues(), 2);
@@ -357,6 +203,11 @@
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" },
+ { "dc= hello world", "dc=hello world", "dc=hello world" },
+ { "undefined=hello", "undefined=hello", "undefined=hello" },
{ "DC=HELLO WORLD", "dc=hello world", "DC=HELLO WORLD" },
{ "dc = hello world", "dc=hello world",
"dc=hello world" },
@@ -417,61 +268,20 @@
/**
- * 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" },
+ return new Object[][] { { null }, { "" }, { " " }, { "=" }, { "manager" },
+ { "manager " }, { "cn+"}, { "cn+Jim" }, { "cn=Jim+" }, { "cn=Jim +" },
+ { "cn=Jim+ " }, { "cn=Jim+sn" }, { "cn=Jim+sn " },
+ { "cn=Jim+sn equals" }, { "cn=Jim," }, { "cn=Jim;" }, { "cn=Jim, " },
+ { "cn=Jim+sn=a," }, { "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" },
+ { "cn=Jim+sn=Bob++" }, { "cn=Jim+sn=Bob+," },
{ "1.3.6.1.4.1.1466..0=#04024869" }, };
}
@@ -495,41 +305,6 @@
/**
- * 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
@@ -537,11 +312,12 @@
*/
@Test
public void testGetAttributeName() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
+ AttributeType[] attrTypes = { AT_DC, AT_CN };
+ String[] attrNames = { AT_DC.getNameOrOID(),
+ AT_CN.getNameOrOID() };
+ AttributeValue[] attrValues = { AV_DC_ORG, AV_CN };
- builder.append(AT_DC, AV_DC_ORG);
- builder.append(AT_CN, AV_CN);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(attrTypes, attrNames, attrValues);
assertEquals(rdn.getAttributeName(0), AT_DC.getNameOrOID());
assertEquals(rdn.getAttributeName(1), AT_CN.getNameOrOID());
@@ -557,10 +333,8 @@
*/
@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 rdn = new RDN(new AttributeType[0], new String[0],
+ new AttributeValue[0]);
rdn.getAttributeName(1);
}
@@ -575,11 +349,12 @@
*/
@Test
public void testGetAttributeType() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
+ AttributeType[] attrTypes = { AT_DC, AT_CN };
+ String[] attrNames = { AT_DC.getNameOrOID(),
+ AT_CN.getNameOrOID() };
+ AttributeValue[] attrValues = { AV_DC_ORG, AV_CN };
- builder.append(AT_DC, AV_DC_ORG);
- builder.append(AT_CN, AV_CN);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(attrTypes, attrNames, attrValues);
assertEquals(rdn.getAttributeType(0), AT_DC);
assertEquals(rdn.getAttributeType(1), AT_CN);
@@ -595,10 +370,8 @@
*/
@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 rdn = new RDN(new AttributeType[0], new String[0],
+ new AttributeValue[0]);
rdn.getAttributeType(1);
}
@@ -613,11 +386,12 @@
*/
@Test
public void testGetAttributeValue() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
+ AttributeType[] attrTypes = { AT_DC, AT_CN };
+ String[] attrNames = { AT_DC.getNameOrOID(),
+ AT_CN.getNameOrOID() };
+ AttributeValue[] attrValues = { AV_DC_ORG, AV_CN };
- builder.append(AT_DC, AV_DC_ORG);
- builder.append(AT_CN, AV_CN);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(attrTypes, attrNames, attrValues);
assertEquals(rdn.getAttributeValue(0), AV_DC_ORG);
assertEquals(rdn.getAttributeValue(1), AV_CN);
@@ -633,10 +407,8 @@
*/
@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 rdn = new RDN(new AttributeType[0], new String[0],
+ new AttributeValue[0]);
rdn.getAttributeValue(1);
}
@@ -651,10 +423,7 @@
*/
@Test
public void testGetAttributeValueByType() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
-
- builder.append(AT_DC, AV_DC_ORG);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
assertEquals(rdn.getAttributeValue(AT_DC), AV_DC_ORG);
assertNull(rdn.getAttributeValue(AT_CN));
@@ -670,14 +439,10 @@
*/
@Test
public void testGetNumValues() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
-
- builder.append(AT_DC, AV_DC_ORG);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
assertEquals(rdn.getNumValues(), 1);
- builder.append(AT_CN, AV_CN);
- rdn = builder.getInstance();
+ rdn.addValue(AT_CN, AT_CN.getNameOrOID(), AV_CN);
assertEquals(rdn.getNumValues(), 2);
}
@@ -690,13 +455,14 @@
* 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();
+ public void testHasAttributeType1() throws Exception {
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
assertTrue(rdn.hasAttributeType(AT_DC));
+ assertTrue(rdn.hasAttributeType("dc"));
+ assertTrue(rdn.hasAttributeType(AT_DC.getOID()));
assertFalse(rdn.hasAttributeType(AT_CN));
+ assertFalse(rdn.hasAttributeType("cn"));
}
@@ -709,20 +475,50 @@
*/
@Test
public void testIsMultiValued() throws Exception {
- RDN.Builder builder = RDN.createBuilder();
-
- builder.append(AT_DC, AV_DC_ORG);
- RDN rdn = builder.getInstance();
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
+ assertEquals(rdn.getNumValues(), 1);
assertFalse(rdn.isMultiValued());
- builder.append(AT_CN, AV_CN);
- rdn = builder.getInstance();
+ rdn.addValue(AT_CN, AT_CN.getNameOrOID(), AV_CN);
assertTrue(rdn.isMultiValued());
}
/**
+ * Tests hasValue.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testHasValue() throws Exception {
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
+ assertTrue(rdn.hasValue(AT_DC, AV_DC_ORG));
+ assertFalse(rdn.hasValue(AT_CN, AV_CN));
+
+ rdn.addValue(AT_CN, AT_CN.getNameOrOID(), AV_CN);
+ assertTrue(rdn.hasValue(AT_DC, AV_DC_ORG));
+ assertTrue(rdn.hasValue(AT_CN, AV_CN));
+ }
+
+
+
+ /**
+ * Tests addValue with a duplicate value.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testAddDuplicateValue() throws Exception {
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
+ assertFalse(rdn.addValue(AT_DC, AT_DC.getNameOrOID(), AV_DC_ORG));
+ }
+
+
+
+ /**
* Test RDN string decoder.
*
* @param rawRDN
@@ -744,6 +540,44 @@
/**
+ * Tests the duplicate method with a single-valued RDN.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testDuplicateSingle() {
+ RDN rdn1 = new RDN(AT_DC, AV_DC_ORG);
+ RDN rdn2 = rdn1.duplicate();
+
+ assertFalse(rdn1 == rdn2);
+ assertEquals(rdn1, rdn2);
+ }
+
+
+
+ /**
+ * Tests the duplicate method with a multivalued RDN.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testDuplicateMultiValued() {
+ AttributeType[] types = new AttributeType[] { AT_DC, AT_CN };
+ String[] names = new String[] { "dc", "cn" };
+ AttributeValue[] values = new AttributeValue[] { AV_DC_ORG, AV_CN };
+
+ RDN rdn1 = new RDN(types, names, values);
+ RDN rdn2 = rdn1.duplicate();
+
+ assertFalse(rdn1 == rdn2);
+ assertEquals(rdn1, rdn2);
+ }
+
+
+
+ /**
* RDN equality test data provider.
*
* @return The array of test RDN strings.
@@ -758,6 +592,13 @@
{ "cn=hello world\\ ", "cn=hello world", 0 },
{ "cn=HELLO WORLD", "cn=hello world", 0 },
{ "cn=HELLO+sn=WORLD", "sn=world+cn=hello", 0 },
+ { "cn=HELLO+sn=WORLD", "cn=hello+sn=nurse", 1 },
+ { "cn=HELLO+sn=WORLD", "cn=howdy+sn=yall", -1 },
+ { "cn=hello", "cn=hello+sn=world", -1 },
+ { "cn=hello+sn=world", "cn=hello", 1 },
+ { "cn=hello+sn=world", "cn=hello+description=world", 1 },
+ { "cn=hello", "sn=world", -1 },
+ { "sn=hello", "cn=world", 1 },
{ "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 },
@@ -866,4 +707,34 @@
+ second + ">.");
}
+
+
+ /**
+ * Tests the equals method with a null argument.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testEqualityNull() {
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
+
+ assertFalse(rdn.equals(null));
+ }
+
+
+
+ /**
+ * Tests the equals method with a non-RDN argument.
+ *
+ * @throws Exception
+ * If the test failed unexpectedly.
+ */
+ @Test
+ public void testEqualityNonRDN() {
+ RDN rdn = new RDN(AT_DC, AV_DC_ORG);
+
+ assertFalse(rdn.equals("this isn't an RDN"));
+ }
}
+
--
Gitblit v1.10.0