DN.java revision 6870993d12bf8a2b9d5cd103dc5ccabc42f9bf5d
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * CDDL HEADER START
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The contents of this file are subject to the terms of the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Common Development and Distribution License, Version 1.0 only
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * (the "License"). You may not use this file except in compliance
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * with the License.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * See the License for the specific language governing permissions
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * and limitations under the License.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * When distributing Covered Code, include this CDDL HEADER in each
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * file and include the License file at legal-notices/CDDLv1_0.txt.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If applicable, add the following below this CDDL HEADER, with the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * fields enclosed by brackets "[]" replaced with your own identifying
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * information:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Portions Copyright [yyyy] [name of copyright owner]
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * CDDL HEADER END
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Copyright 2009-2010 Sun Microsystems, Inc.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Portions copyright 2011-2012 ForgeRock AS.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmarkimport static com.forgerock.opendj.util.StaticUtils.getBytes;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmarkimport static com.forgerock.opendj.ldap.CoreMessages.ERR_DN_TYPE_NOT_FOUND;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.i18n.LocalizedIllegalArgumentException;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.opendj.ldap.schema.UnknownSchemaElementException;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * A distinguished name (DN) as defined in RFC 4512 section 2.3 is the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * concatenation of its relative distinguished name (RDN) and its immediate
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * superior's DN. A DN unambiguously refers to an entry in the Directory.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The following are examples of string representations of DNs:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * UID=nobody@example.com,DC=example,DC=com CN=John
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Smith,OU=Sales,O=ACME Limited,L=Moab,ST=Utah,C=US
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see <a href="http://tools.ietf.org/html/rfc4512#section-2.3">RFC 4512 -
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Lightweight Directory Access Protocol (LDAP): Directory Information
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Models </a>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkpublic final class DN implements Iterable<RDN>, Comparable<DN> {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private static final DN ROOT_DN = new DN(null, null, "");
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * This is the size of the per-thread per-schema DN cache. We should
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * be conservative here in case there are many threads. We will only
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * cache parent DNs, so there's no need for it to be big.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private static final ThreadLocal<WeakHashMap<Schema, Map<String, DN>>> CACHE =
08248b5c5b494aff8d1922e8e0b5777796d7450dmark new ThreadLocal<WeakHashMap<Schema, Map<String, DN>>>() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark protected WeakHashMap<Schema, Map<String, DN>> initialValue() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the LDAP string representation of the provided DN attribute value
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * in a form suitable for substitution directly into a DN string. This
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * method may be useful in cases where a DN is to be constructed from a DN
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * template using {@code String#format(String, Object...)}. The following
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * example illustrates two approaches to constructing a DN:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * // This may contain user input.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String attributeValue = ...;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * // Using the equality filter constructor:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN dn = DN.valueOf("ou=people,dc=example,dc=com").child("uid", attributeValue);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * // Using a String template:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String dnTemplate = "uid=%s,ou=people,dc=example,dc=com";
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String dnString = String.format(dnTemplate,
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN.escapeAttributeValue(attributeValue));
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN dn = DN.valueOf(dnString);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <b>Note:</b> attribute values do not and should not be escaped before
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * passing them to constructors like {@link #child(String, Object)}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Escaping is only required when creating DN strings.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param attributeValue
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The attribute value.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The LDAP string representation of the provided filter assertion
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * value in a form suitable for substitution directly into a filter
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static String escapeAttributeValue(final Object attributeValue) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final StringBuilder builder = new StringBuilder(s.length());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Creates a new DN using the provided DN template and unescaped attribute
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * values using the default schema. This method first escapes each of the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * attribute values and then substitutes them into the template using
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * {@link String#format(String, Object...)}. Finally, the formatted string
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * is parsed as an LDAP DN using {@link #valueOf(String)}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * This method may be useful in cases where the structure of a DN is not
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * known at compile time, for example, it may be obtained from a
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * configuration file. Example usage:
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * String template = "uid=%s,ou=people,dc=example,dc=com";
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN dn = DN.format(template, "bjensen");
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param template
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The DN template.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param attributeValues
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The attribute values to be substituted into the template.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The formatted template parsed as a {@code DN}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If the formatted template is not a valid LDAP string
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * representation of a DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #escapeAttributeValue(Object)
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN format(final String template, final Object... attributeValues) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return format(template, Schema.getDefaultSchema(), attributeValues);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Creates a new DN using the provided DN template and unescaped attribute
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * values using the provided schema. This method first escapes each of the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * attribute values and then substitutes them into the template using
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * {@link String#format(String, Object...)}. Finally, the formatted string
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * is parsed as an LDAP DN using {@link #valueOf(String)}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * This method may be useful in cases where the structure of a DN is not
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * known at compile time, for example, it may be obtained from a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * configuration file. Example usage:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String template = "uid=%s,ou=people,dc=example,dc=com";
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN dn = DN.format(template, "bjensen");
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param template
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The DN template.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param schema
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The schema to use when parsing the DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param attributeValues
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The attribute values to be substituted into the template.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The formatted template parsed as a {@code DN}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If the formatted template is not a valid LDAP string
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * representation of a DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #escapeAttributeValue(Object)
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN format(final String template, final Schema schema,
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String[] attributeValueStrings = new String[attributeValues.length];
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark attributeValueStrings[i] = escapeAttributeValue(attributeValues[i]);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String dnString = String.format(template, (Object[]) attributeValueStrings);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the Root DN. The Root DN does not contain and RDN components and
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * is superior to all other DNs.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Parses the provided LDAP string representation of a DN using the default
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The LDAP string representation of a DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The parsed DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #format(String, Object...)
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Parses the provided LDAP string representation of a DN using the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The LDAP string representation of a DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param schema
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The schema to use when parsing the DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The parsed DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} or {@code schema} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #format(String, Schema, Object...)
97cb8289f277962530b3890287205dca5401bb4amark public static DN valueOf(final String dn, final Schema schema) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // First check if DN is already cached.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Not in cache so decode.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final SubstringReader reader = new SubstringReader(dn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Compares the provided DN values to determine their relative order in a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * sorted list.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn1
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The first DN to be compared. It must not be {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn2
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The second DN to be compared. It must not be {@code null}.
97cb8289f277962530b3890287205dca5401bb4amark * @return A negative integer if the first DN should come before the second
97cb8289f277962530b3890287205dca5401bb4amark * DN in a sorted list, a positive integer if the first DN should
97cb8289f277962530b3890287205dca5401bb4amark * come after the second DN in a sorted list, or zero if the two DN
97cb8289f277962530b3890287205dca5401bb4amark * values can be considered equal.
97cb8289f277962530b3890287205dca5401bb4amark private static int compareTo(final DN dn1, final DN dn2) {
97cb8289f277962530b3890287205dca5401bb4amark // Quickly check if we are comparing against root dse.
97cb8289f277962530b3890287205dca5401bb4amark // both are equal.
97cb8289f277962530b3890287205dca5401bb4amark // dn1 comes before dn2.
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark // dn1 comes after dn2.
97cb8289f277962530b3890287205dca5401bb4amark final int result = dn1Parent.rdn.compareTo(dn2Parent.rdn);
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark // What do we have here?
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark /** Decodes a DN using the provided reader and schema. */
97cb8289f277962530b3890287205dca5401bb4amark private static DN decode(final String dnString, final SubstringReader reader,
97cb8289f277962530b3890287205dca5401bb4amark } catch (final UnknownSchemaElementException e) {
97cb8289f277962530b3890287205dca5401bb4amark ERR_DN_TYPE_NOT_FOUND.get(reader.getString(), e.getMessageObject());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String parentString = reader.read(reader.remaining());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Only cache parent DNs since leaf DNs are likely to make the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // cache to volatile.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark private static Map<String, DN> getCache(final Schema schema) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark final WeakHashMap<Schema, Map<String, DN>> threadLocalMap = CACHE.get();
ec40cc0dc62425cea5d63fd9d984f8614479de25mark Map<String, DN> schemaLocalMap = threadLocalMap.get(schema);
ec40cc0dc62425cea5d63fd9d984f8614479de25mark schemaLocalMap = new LinkedHashMap<String, DN>(DN_CACHE_SIZE, 0.75f, true) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark protected boolean removeEldestEntry(final Map.Entry<String, DN> e) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private final int size;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * We need to store the original string value if provided in order to
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * preserve the original whitespace.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /** Private constructor. */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private DN(final DN parent, final RDN rdn, final String stringValue) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark this(parent, rdn, stringValue, parent != null ? parent.size + 1 : 0);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /** Private constructor. */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private DN(final DN parent, final RDN rdn, final String stringValue, final int size) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns a DN which is subordinate to this DN and having the additional
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * RDN components contained in the provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The DN containing the RDN components to be added to this DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The subordinate DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code dn} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return this;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else if (isRootDN()) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark for (DN next = dn; next.rdn != null; next = next.parent) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns a DN which is an immediate child of this DN and having the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * specified RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <b>Note:</b> the child DN whose RDN is {@link RDN#maxValue()} compares
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * greater than all other possible child DNs, and may be used to construct
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * range queries against DN keyed sorted collections such as
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * {@code SortedSet} and {@code SortedMap}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param rdn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The RDN for the child DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The child DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code rdn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see RDN#maxValue()
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns a DN which is subordinate to this DN and having the additional
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * RDN components contained in the provided DN decoded using the default
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The DN containing the RDN components to be added to this DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The subordinate DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns a DN which is an immediate child of this DN and with an RDN
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * having the provided attribute type and value decoded using the default
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code attributeValue} is not an instance of {@code ByteString} then
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * it will be converted using the {@link ByteString#valueOf(Object)} method.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param attributeType
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The attribute type.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param attributeValue
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The attribute value.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The child DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws UnknownSchemaElementException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code attributeType} was not found in the default schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code attributeType} or {@code attributeValue} was
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public DN child(final String attributeType, final Object attributeValue) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (this == obj) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return true;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return true;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return false;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public int hashCode() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is an immediate child of the provided DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The potential parent DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is the immediate child of the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // If this is the Root DN then parent will be null but this is ok.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is an immediate child of the provided DN
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * decoded using the default schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The potential parent DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is the immediate child of the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // If this is the Root DN then parent will be null but this is ok.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN matches the provided base DN and search
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The base DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param scope
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The search scope.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return {@code true} if this DN matches the provided base DN and search
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * scope, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} or {@code scope} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // The base DN must equal this DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // The parent DN must equal the base DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This DN must be a descendant of the provided base DN, but
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // not equal to it.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This DN must be a descendant of the provided base DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This is a scope that we don't recognize.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return false;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN matches the provided base DN and search
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The base DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param scope
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The search scope.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN matches the provided base DN and search
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * scope, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} or {@code scope} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isInScopeOf(String dn, SearchScope scope) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is the immediate parent of the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The potential child DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is the immediate parent of the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // If dn is the Root DN then parent will be null but this is ok.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is the immediate parent of the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The potential child DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return {@code true} if this DN is the immediate parent of the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // If dn is the Root DN then parent will be null but this is ok.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is the Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is the Root DN, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isRootDN() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is subordinate to or equal to the
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The potential child DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return {@code true} if this DN is subordinate to or equal to the
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * provided DN, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return false;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // dn is a potential superior of this.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is subordinate to or equal to the
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * provided DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The potential child DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is subordinate to or equal to the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * provided DN, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws LocalizedIllegalArgumentException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isSubordinateOrEqualTo(final String dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is superior to or equal to the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param dn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The potential child DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is superior to or equal to the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code dn} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return false;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // dn is a potential subordinate of this.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is superior to or equal to the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param dn
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The potential child DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return {@code true} if this DN is superior to or equal to the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN, otherwise {@code false}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws LocalizedIllegalArgumentException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code dn} is not a valid LDAP string representation of a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * If {@code dn} was {@code null}.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * Returns an iterator of the RDNs contained in this DN. The RDNs will be
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * returned in the order starting with this DN's RDN, followed by the RDN of
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * the parent DN, and so on.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * Attempts to remove RDNs using an iterator's {@code remove()} method are
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * not permitted and will result in an {@code UnsupportedOperationException}
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * being thrown.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @return An iterator of the RDNs contained in this DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public boolean hasNext() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public void remove() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * Returns the DN whose content is the specified number of RDNs from this
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * DN. The following equivalences hold:
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(0).isRootDN();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(1).equals(rootDN.child(dn.rdn()));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(dn.size()).equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @param index
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * The number of RDNs to be included in the local name.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The DN whose content is the specified number of RDNs from this
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @throws IllegalArgumentException
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * If {@code index} is less than zero.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return this;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark nextLocalName.parent = new DN(null, lastDN.rdn, null, i);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the DN which is the immediate parent of this DN, or {@code null}
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * if this DN is the Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * This method is equivalent to:
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * parent(1);
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @return The DN which is the immediate parent of this DN, or {@code null}
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * if this DN is the Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the DN which is equal to this DN with the specified number of
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * RDNs removed. Note that if {@code index} is zero then this DN will be
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * returned (identity).
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param index
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The number of RDNs to be removed.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The DN which is equal to this DN with the specified number of
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * RDNs removed, or {@code null} if the parent of the Root DN is
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * reached.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * @throws IllegalArgumentException
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * If {@code index} is less than zero.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // We allow size + 1 so that we can return null as the parent of the
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the RDN of this DN, or {@code null} if this DN is the Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The RDN of this DN, or {@code null} if this DN is the Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns a copy of this DN whose parent DN, {@code fromDN}, has been
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * renamed to the new parent DN, {@code toDN}. If this DN is not subordinate
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * or equal to {@code fromDN} then this DN is returned (i.e. the DN is not
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * renamed).
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * @param fromDN
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * The old parent DN.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark * @param toDN
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The new parent DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The renamed DN, or this DN if no renaming was performed.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @throws NullPointerException
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * If {@code fromDN} or {@code toDN} was {@code null}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return this;
bae06e40ce897cdf589169079718c3e70af3684bmark * Returns the number of RDN components in this DN.
bae06e40ce897cdf589169079718c3e70af3684bmark * @return The number of RDN components in this DN.
bae06e40ce897cdf589169079718c3e70af3684bmark public int size() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the RFC 4514 string representation of this DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The RFC 4514 string representation of this DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see <a href="http://tools.ietf.org/html/rfc4514">RFC 4514 - Lightweight
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Directory Access Protocol (LDAP): String Representation of
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Distinguished Names </a>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // We don't care about potential race conditions here.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the normalized string representation of a DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The normalized string representation of the provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final StringBuilder builder = new StringBuilder(this.size());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark for (i--; i >= 0; i--) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // Only add a separator if the RDN is not RDN.maxValue().
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the normalized string representation of a RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param builder
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The StringBuilder to use to construct the normalized string.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param rdn
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The normalized string representation of the provided RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private static StringBuilder normalizeRDN(final StringBuilder builder, final RDN rdn) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark switch (sz) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Handle RDN.maxValue().
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Need to sort the AVAs before comparing.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Normalize the first AVA.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark while (i.hasNext()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the normalized string representation of an AVA.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param builder
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The StringBuilder to use to construct the normalized string.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @param ava
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The AVA.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The normalized string representation of the provided AVA.
1c87ba56eccd769f09e5cb68104bbcbab21f2845mark private static StringBuilder normalizeAVA(final StringBuilder builder, final AVA ava) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final MatchingRule matchingRule = ava.getAttributeType().getEqualityMatchingRule();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark value = matchingRule.normalizeAttributeValue(ava.getAttributeValue());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // Ignore - we'll drop back to the user provided value.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (!ava.getAttributeType().getNames().iterator().hasNext()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String name = ava.getAttributeType().getNameOrOID();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Normalizing.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final Syntax syntax = ava.getAttributeType().getSyntax();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (c < ' ') {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark || (c == '"' || c == '+' || c == ',' || c == ';' || c == '<'
08248b5c5b494aff8d1922e8e0b5777796d7450dmark || c == '=' || c == '>' || c == '\\' || c == '\u0000')) {