DN.java revision 6870993d12bf8a2b9d5cd103dc5ccabc42f9bf5d
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark/*
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * CDDL HEADER START
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * or http://forgerock.org/license/CDDLv1.0.html.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * See the License for the specific language governing permissions
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * and limitations under the License.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * CDDL HEADER END
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
391d13679315472c5e7b2abcde000787152da4c6mark *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Copyright 2009-2010 Sun Microsystems, Inc.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Portions copyright 2011-2012 ForgeRock AS.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkpackage org.forgerock.opendj.ldap;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmarkimport static com.forgerock.opendj.util.StaticUtils.getBytes;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmarkimport static com.forgerock.opendj.ldap.CoreMessages.ERR_DN_TYPE_NOT_FOUND;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.Iterator;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.LinkedHashMap;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.Map;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.NoSuchElementException;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.TreeSet;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport java.util.WeakHashMap;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.i18n.LocalizableMessage;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.i18n.LocalizedIllegalArgumentException;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.opendj.ldap.schema.MatchingRule;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.opendj.ldap.schema.Schema;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.opendj.ldap.schema.Syntax;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.opendj.ldap.schema.UnknownSchemaElementException;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport org.forgerock.util.Reject;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport com.forgerock.opendj.util.StaticUtils;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkimport com.forgerock.opendj.util.SubstringReader;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark/**
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 * <p>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * The following are examples of string representations of DNs:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <pre>
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 * </pre>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmarkpublic final class DN implements Iterable<RDN>, Comparable<DN> {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private static final DN ROOT_DN = new DN(null, null, "");
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
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 */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private static final int DN_CACHE_SIZE = 32;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private static final ThreadLocal<WeakHashMap<Schema, Map<String, DN>>> CACHE =
08248b5c5b494aff8d1922e8e0b5777796d7450dmark new ThreadLocal<WeakHashMap<Schema, Map<String, DN>>>() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark @Override
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark protected WeakHashMap<Schema, Map<String, DN>> initialValue() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return new WeakHashMap<Schema, Map<String, DN>>();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark };
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
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 *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <pre>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * // This may contain user input.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String attributeValue = ...;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * // Using the equality filter constructor:
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN dn = DN.valueOf("ou=people,dc=example,dc=com").child("uid", attributeValue);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * </pre>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * string.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static String escapeAttributeValue(final Object attributeValue) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark Reject.ifNull(attributeValue);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String s = String.valueOf(attributeValue);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final StringBuilder builder = new StringBuilder(s.length());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark AVA.escapeAttributeValue(s, builder);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return builder.toString();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
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 * <p>
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 *
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * <pre>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * String template = &quot;uid=%s,ou=people,dc=example,dc=com&quot;;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN dn = DN.format(template, &quot;bjensen&quot;);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * </pre>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN format(final String template, final Object... attributeValues) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return format(template, Schema.getDefaultSchema(), attributeValues);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
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 * <p>
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 *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <pre>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * String template = &quot;uid=%s,ou=people,dc=example,dc=com&quot;;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN dn = DN.format(template, &quot;bjensen&quot;);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * </pre>
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN format(final String template, final Schema schema,
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final Object... attributeValues) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String[] attributeValueStrings = new String[attributeValues.length];
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark for (int i = 0; i < attributeValues.length; i++) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark attributeValueStrings[i] = escapeAttributeValue(attributeValues[i]);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String dnString = String.format(template, (Object[]) attributeValueStrings);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return valueOf(dnString, schema);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the Root DN. The Root DN does not contain and RDN components and
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * is superior to all other DNs.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return The Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN rootDN() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return ROOT_DN;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Parses the provided LDAP string representation of a DN using the default
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #format(String, Object...)
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public static DN valueOf(final String dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return valueOf(dn, Schema.getDefaultSchema());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Parses the provided LDAP string representation of a DN using the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} or {@code schema} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @see #format(String, Schema, Object...)
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
97cb8289f277962530b3890287205dca5401bb4amark public static DN valueOf(final String dn, final Schema schema) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark Reject.ifNull(dn, schema);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (dn.length() == 0) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return ROOT_DN;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // First check if DN is already cached.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final Map<String, DN> cache = getCache(schema);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final DN cachedDN = cache.get(dn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (cachedDN != null) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return cachedDN;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Not in cache so decode.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final SubstringReader reader = new SubstringReader(dn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return decode(dn, reader, schema, cache);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Compares the provided DN values to determine their relative order in a
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * sorted list.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
97cb8289f277962530b3890287205dca5401bb4amark private static int compareTo(final DN dn1, final DN dn2) {
97cb8289f277962530b3890287205dca5401bb4amark // Quickly check if we are comparing against root dse.
97cb8289f277962530b3890287205dca5401bb4amark if (dn1.isRootDN()) {
97cb8289f277962530b3890287205dca5401bb4amark if (dn2.isRootDN()) {
97cb8289f277962530b3890287205dca5401bb4amark // both are equal.
97cb8289f277962530b3890287205dca5401bb4amark return 0;
97cb8289f277962530b3890287205dca5401bb4amark } else {
97cb8289f277962530b3890287205dca5401bb4amark // dn1 comes before dn2.
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark
97cb8289f277962530b3890287205dca5401bb4amark if (dn2.isRootDN()) {
97cb8289f277962530b3890287205dca5401bb4amark // dn1 comes after dn2.
97cb8289f277962530b3890287205dca5401bb4amark return 1;
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark
97cb8289f277962530b3890287205dca5401bb4amark int dn1Size = dn1.size - 1;
97cb8289f277962530b3890287205dca5401bb4amark int dn2Size = dn2.size - 1;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark while (dn1Size >= 0 && dn2Size >= 0) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final DN dn1Parent = dn1.parent(dn1Size--);
97cb8289f277962530b3890287205dca5401bb4amark final DN dn2Parent = dn2.parent(dn2Size--);
97cb8289f277962530b3890287205dca5401bb4amark final int result = dn1Parent.rdn.compareTo(dn2Parent.rdn);
97cb8289f277962530b3890287205dca5401bb4amark if (result > 0) {
97cb8289f277962530b3890287205dca5401bb4amark return 1;
97cb8289f277962530b3890287205dca5401bb4amark } else if (result < 0) {
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark
97cb8289f277962530b3890287205dca5401bb4amark // What do we have here?
97cb8289f277962530b3890287205dca5401bb4amark if (dn1Size > dn2Size) {
97cb8289f277962530b3890287205dca5401bb4amark return 1;
97cb8289f277962530b3890287205dca5401bb4amark } else if (dn1Size < dn2Size) {
97cb8289f277962530b3890287205dca5401bb4amark return -1;
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark
97cb8289f277962530b3890287205dca5401bb4amark return 0;
97cb8289f277962530b3890287205dca5401bb4amark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
97cb8289f277962530b3890287205dca5401bb4amark /** Decodes a DN using the provided reader and schema. */
97cb8289f277962530b3890287205dca5401bb4amark private static DN decode(final String dnString, final SubstringReader reader,
97cb8289f277962530b3890287205dca5401bb4amark final Schema schema, final Map<String, DN> cache) {
97cb8289f277962530b3890287205dca5401bb4amark reader.skipWhitespaces();
97cb8289f277962530b3890287205dca5401bb4amark if (reader.remaining() == 0) {
97cb8289f277962530b3890287205dca5401bb4amark return ROOT_DN;
97cb8289f277962530b3890287205dca5401bb4amark }
97cb8289f277962530b3890287205dca5401bb4amark
97cb8289f277962530b3890287205dca5401bb4amark RDN rdn;
97cb8289f277962530b3890287205dca5401bb4amark try {
97cb8289f277962530b3890287205dca5401bb4amark rdn = RDN.decode(null, reader, schema);
97cb8289f277962530b3890287205dca5401bb4amark } catch (final UnknownSchemaElementException e) {
97cb8289f277962530b3890287205dca5401bb4amark final LocalizableMessage message =
97cb8289f277962530b3890287205dca5401bb4amark ERR_DN_TYPE_NOT_FOUND.get(reader.getString(), e.getMessageObject());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark throw new LocalizedIllegalArgumentException(message);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark DN parent;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (reader.remaining() > 0 && reader.read() == ',') {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark reader.mark();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String parentString = reader.read(reader.remaining());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark parent = cache.get(parentString);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (parent == null) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark reader.reset();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark parent = decode(parentString, reader, schema, cache);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Only cache parent DNs since leaf DNs are likely to make the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // cache to volatile.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark cache.put(parentString, parent);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark parent = ROOT_DN;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark }
ec40cc0dc62425cea5d63fd9d984f8614479de25mark
ec40cc0dc62425cea5d63fd9d984f8614479de25mark return new DN(parent, rdn, dnString);
ec40cc0dc62425cea5d63fd9d984f8614479de25mark }
ec40cc0dc62425cea5d63fd9d984f8614479de25mark
ec40cc0dc62425cea5d63fd9d984f8614479de25mark @SuppressWarnings("serial")
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
ec40cc0dc62425cea5d63fd9d984f8614479de25mark if (schemaLocalMap == null) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark schemaLocalMap = new LinkedHashMap<String, DN>(DN_CACHE_SIZE, 0.75f, true) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark @Override
ec40cc0dc62425cea5d63fd9d984f8614479de25mark protected boolean removeEldestEntry(final Map.Entry<String, DN> e) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark return size() > DN_CACHE_SIZE;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark };
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark threadLocalMap.put(schema, schemaLocalMap);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return schemaLocalMap;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private final RDN rdn;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private DN parent;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private final int size;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * We need to store the original string value if provided in order to
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * preserve the original whitespace.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private String stringValue;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
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 }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /** Private constructor. */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark private DN(final DN parent, final RDN rdn, final String stringValue, final int size) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark this.parent = parent;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark this.rdn = rdn;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark this.stringValue = stringValue;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark this.size = size;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns a DN which is subordinate to this DN and having the additional
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * RDN components contained in the provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public DN child(final DN dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark Reject.ifNull(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (dn.isRootDN()) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return this;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else if (isRootDN()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return dn;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final RDN[] rdns = new RDN[dn.size()];
08248b5c5b494aff8d1922e8e0b5777796d7450dmark int i = rdns.length;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark for (DN next = dn; next.rdn != null; next = next.parent) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark rdns[--i] = next.rdn;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark DN newDN = this;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark for (i = 0; i < rdns.length; i++) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark newDN = new DN(newDN, rdns[i], null);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return newDN;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns a DN which is an immediate child of this DN and having the
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * specified RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * <p>
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 *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public DN child(final RDN rdn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark Reject.ifNull(rdn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return new DN(this, rdn, null);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
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
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public DN child(final String dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark Reject.ifNull(dn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return child(valueOf(dn));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
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 * schema.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * <p>
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 *
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 */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public DN child(final String attributeType, final Object attributeValue) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return child(new RDN(attributeType, attributeValue));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /** {@inheritDoc} */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public int compareTo(final DN dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return compareTo(this, dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark @Override
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean equals(final Object obj) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (this == obj) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return true;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else if (obj instanceof DN) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark DN other = (DN) obj;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (size == other.size()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (size == 0) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return true;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (rdn.equals(other.rdn)) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return parent.equals(other.parent);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return false;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /** {@inheritDoc} */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark @Override
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public int hashCode() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (size == 0) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return 0;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return 31 * parent.hashCode() + rdn.hashCode();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is an immediate child of the provided DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isChildOf(final DN dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // If this is the Root DN then parent will be null but this is ok.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return dn.equals(parent);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is an immediate child of the provided DN
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * decoded using the default schema.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public boolean isChildOf(final String dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // If this is the Root DN then parent will be null but this is ok.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return isChildOf(valueOf(dn));
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN matches the provided base DN and search
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * scope.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isInScopeOf(DN dn, SearchScope scope) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (scope == SearchScope.BASE_OBJECT) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // The base DN must equal this DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return equals(dn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else if (scope == SearchScope.SINGLE_LEVEL) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // The parent DN must equal the base DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return isChildOf(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else if (scope == SearchScope.SUBORDINATES) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This DN must be a descendant of the provided base DN, but
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // not equal to it.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return isSubordinateOrEqualTo(dn) && !equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else if (scope == SearchScope.WHOLE_SUBTREE) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This DN must be a descendant of the provided base DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return isSubordinateOrEqualTo(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // This is a scope that we don't recognize.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return false;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN matches the provided base DN and search
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * scope.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} or {@code scope} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isInScopeOf(String dn, SearchScope scope) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return isInScopeOf(valueOf(dn), scope);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is the immediate parent of the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isParentOf(final DN dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // If dn is the Root DN then parent will be null but this is ok.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return equals(dn.parent);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is the immediate parent of the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public boolean isParentOf(final String dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // If dn is the Root DN then parent will be null but this is ok.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return isParentOf(valueOf(dn));
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is the Root DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @return {@code true} if this DN is the Root DN, otherwise {@code false}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isRootDN() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return size == 0;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is subordinate to or equal to the
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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}.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isSubordinateOrEqualTo(final DN dn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (size < dn.size) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return false;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else if (size == dn.size) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // dn is a potential superior of this.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return parent(size - dn.size).equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is subordinate to or equal to the
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * If {@code dn} was {@code null}.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public boolean isSubordinateOrEqualTo(final String dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return isSubordinateOrEqualTo(valueOf(dn));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns {@code true} if this DN is superior to or equal to the provided
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public boolean isSuperiorOrEqualTo(final DN dn) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (size > dn.size) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return false;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else if (size == dn.size) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // dn is a potential subordinate of this.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return dn.parent(dn.size - size).equals(this);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns {@code true} if this DN is superior to or equal to the provided
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 * DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * @throws NullPointerException
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * If {@code dn} was {@code null}.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark */
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public boolean isSuperiorOrEqualTo(final String dn) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return isSuperiorOrEqualTo(valueOf(dn));
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark /**
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 * <p>
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 *
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @return An iterator of the RDNs contained in this DN.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark */
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public Iterator<RDN> iterator() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return new Iterator<RDN>() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark private DN dn = DN.this;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public boolean hasNext() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return dn.rdn != null;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public RDN next() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark if (dn.rdn == null) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark throw new NoSuchElementException();
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark final RDN rdn = dn.rdn;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark dn = dn.parent;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return rdn;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public void remove() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark throw new UnsupportedOperationException();
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark };
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark /**
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * Returns the DN whose content is the specified number of RDNs from this
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * DN. The following equivalences hold:
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark *
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * <pre>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(0).isRootDN();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(1).equals(rootDN.child(dn.rdn()));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * dn.localName(dn.size()).equals(dn);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * </pre>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
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 * DN.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @throws IllegalArgumentException
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * If {@code index} is less than zero.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public DN localName(final int index) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark Reject.ifFalse(index >= 0, "index less than zero");
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark if (index == 0) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return ROOT_DN;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark } else if (index >= size) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return this;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark } else {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark final DN localName = new DN(null, rdn, null, index);
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark DN nextLocalName = localName;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark DN lastDN = parent;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark for (int i = index - 1; i > 0; i--) {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark nextLocalName.parent = new DN(null, lastDN.rdn, null, i);
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark nextLocalName = nextLocalName.parent;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark lastDN = lastDN.parent;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark nextLocalName.parent = ROOT_DN;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return localName;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the DN which is the immediate parent of this DN, or {@code null}
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * if this DN is the Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * <p>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * This method is equivalent to:
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * <pre>
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * parent(1);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * </pre>
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark *
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * @return The DN which is the immediate parent of this DN, or {@code null}
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark * if this DN is the Root DN.
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark */
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark public DN parent() {
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark return parent;
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark }
089bd21b4d1cee267b5ca4663cb8a2fe6c029e1cmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
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 *
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 */
ec40cc0dc62425cea5d63fd9d984f8614479de25mark public DN parent(final int index) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // We allow size + 1 so that we can return null as the parent of the
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Root DN.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark Reject.ifFalse(index >= 0, "index less than zero");
ec40cc0dc62425cea5d63fd9d984f8614479de25mark
ec40cc0dc62425cea5d63fd9d984f8614479de25mark DN parentDN = this;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark for (int i = 0; parentDN != null && i < index; i++) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark parentDN = parentDN.parent;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark }
ec40cc0dc62425cea5d63fd9d984f8614479de25mark return parentDN;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark }
ec40cc0dc62425cea5d63fd9d984f8614479de25mark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the RDN of this DN, or {@code null} if this DN is the Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The RDN of this DN, or {@code null} if this DN is the Root DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public RDN rdn() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return rdn;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
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 *
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 */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public DN rename(final DN fromDN, final DN toDN) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark Reject.ifNull(fromDN, toDN);
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (!isSubordinateOrEqualTo(fromDN)) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return this;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else if (equals(fromDN)) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return toDN;
bae06e40ce897cdf589169079718c3e70af3684bmark } else {
bae06e40ce897cdf589169079718c3e70af3684bmark return toDN.child(localName(size - fromDN.size));
bae06e40ce897cdf589169079718c3e70af3684bmark }
bae06e40ce897cdf589169079718c3e70af3684bmark }
bae06e40ce897cdf589169079718c3e70af3684bmark
bae06e40ce897cdf589169079718c3e70af3684bmark /**
bae06e40ce897cdf589169079718c3e70af3684bmark * Returns the number of RDN components in this DN.
bae06e40ce897cdf589169079718c3e70af3684bmark *
bae06e40ce897cdf589169079718c3e70af3684bmark * @return The number of RDN components in this DN.
bae06e40ce897cdf589169079718c3e70af3684bmark */
bae06e40ce897cdf589169079718c3e70af3684bmark public int size() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return size;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the RFC 4514 string representation of this DN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark @Override
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark public String toString() {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // We don't care about potential race conditions here.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (stringValue == null) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final StringBuilder builder = new StringBuilder();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark rdn.toString(builder);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (!parent.isRootDN()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append(',');
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append(parent.toString());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark stringValue = builder.toString();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return stringValue;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark /**
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * Returns the normalized string representation of a DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark *
08248b5c5b494aff8d1922e8e0b5777796d7450dmark * @return The normalized string representation of the provided DN.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark */
08248b5c5b494aff8d1922e8e0b5777796d7450dmark public String toNormalizedString() {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final StringBuilder builder = new StringBuilder(this.size());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (rdn() == null) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return builder.toString();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark int i = this.size() - 1;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark normalizeRDN(builder, parent(i).rdn());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark for (i--; i >= 0; i--) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final RDN rdn = parent(i).rdn();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // Only add a separator if the RDN is not RDN.maxValue().
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (rdn.size() != 0) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append('\u0000');
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark normalizeRDN(builder, rdn);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark return builder.toString();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the normalized string representation of a RDN.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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 */
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark private static StringBuilder normalizeRDN(final StringBuilder builder, final RDN rdn) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark final int sz = rdn.size();
ec40cc0dc62425cea5d63fd9d984f8614479de25mark switch (sz) {
ec40cc0dc62425cea5d63fd9d984f8614479de25mark case 0:
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Handle RDN.maxValue().
ec40cc0dc62425cea5d63fd9d984f8614479de25mark builder.append('\u0001');
ec40cc0dc62425cea5d63fd9d984f8614479de25mark break;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark case 1:
ec40cc0dc62425cea5d63fd9d984f8614479de25mark normalizeAVA(builder, rdn.getFirstAVA());
ec40cc0dc62425cea5d63fd9d984f8614479de25mark break;
ec40cc0dc62425cea5d63fd9d984f8614479de25mark default:
ec40cc0dc62425cea5d63fd9d984f8614479de25mark // Need to sort the AVAs before comparing.
ec40cc0dc62425cea5d63fd9d984f8614479de25mark TreeSet<AVA> a = new TreeSet<AVA>();
ec40cc0dc62425cea5d63fd9d984f8614479de25mark for (AVA ava : rdn) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark a.add(ava);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark Iterator<AVA> i = a.iterator();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Normalize the first AVA.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark normalizeAVA(builder, i.next());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark while (i.hasNext()) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append('\u0001');
08248b5c5b494aff8d1922e8e0b5777796d7450dmark normalizeAVA(builder, i.next());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark break;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return builder;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark /**
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark * Returns the normalized string representation of an AVA.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark *
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.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark */
1c87ba56eccd769f09e5cb68104bbcbab21f2845mark private static StringBuilder normalizeAVA(final StringBuilder builder, final AVA ava) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark ByteString value = ava.getAttributeValue();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final MatchingRule matchingRule = ava.getAttributeType().getEqualityMatchingRule();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (matchingRule != null) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark try {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark value = matchingRule.normalizeAttributeValue(ava.getAttributeValue());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } catch (final DecodeException de) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark // Ignore - we'll drop back to the user provided value.
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (!ava.getAttributeType().getNames().iterator().hasNext()) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append(ava.getAttributeType().getOID());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append("=#");
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append(value.toHexString());
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark } else {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final String name = ava.getAttributeType().getNameOrOID();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark // Normalizing.
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark StaticUtils.toLowerCase(name, builder);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append("=");
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final Syntax syntax = ava.getAttributeType().getSyntax();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (!syntax.isHumanReadable()) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append("#");
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append(value.toHexString());
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark final String str = value.toString();
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if (str.length() == 0) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return builder;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark char c = str.charAt(0);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark int startPos = 0;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if ((c == ' ') || (c == '#')) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append('\\');
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark builder.append(c);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark startPos = 1;
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark final int length = str.length();
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark for (int si = startPos; si < length; si++) {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark c = str.charAt(si);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark if (c < ' ') {
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark for (final byte b : getBytes(String.valueOf(c))) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append('\\');
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append(StaticUtils.byteToLowerHex(b));
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark } else {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark if ((c == ' ' && si == length - 1)
08248b5c5b494aff8d1922e8e0b5777796d7450dmark || (c == '"' || c == '+' || c == ',' || c == ';' || c == '<'
08248b5c5b494aff8d1922e8e0b5777796d7450dmark || c == '=' || c == '>' || c == '\\' || c == '\u0000')) {
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append('\\');
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark builder.append(c);
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
51607ea01068c9047391e4c8b46bc9dbd0edb7fdmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark return builder;
08248b5c5b494aff8d1922e8e0b5777796d7450dmark }
08248b5c5b494aff8d1922e8e0b5777796d7450dmark
08248b5c5b494aff8d1922e8e0b5777796d7450dmark}
08248b5c5b494aff8d1922e8e0b5777796d7450dmark