mirror of https://github.com/OpenIdentityPlatform/OpenDJ.git

Nicolas Capponi
06.52.2013 c2d0d212b510d8e82b9b123b4d06a80b835c8cd4
opendj-admin/src/main/java/org/opends/server/admin/ManagedObjectPath.java
@@ -28,8 +28,6 @@
package org.opends.server.admin;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -43,67 +41,58 @@
import org.forgerock.opendj.ldap.RDN;
import org.forgerock.opendj.ldap.schema.AttributeType;
import org.opends.server.core.DirectoryServer;
import org.opends.server.types.DirectoryException;
/**
 * A path which can be used to determine the location of a managed
 * object instance.
 * A path which can be used to determine the location of a managed object
 * instance.
 * <p>
 * A path is made up of zero or more elements each of which represents
 * a managed object. Managed objects are arranged hierarchically with
 * the root configuration being the top-most managed object. Elements
 * are ordered such that the root configuration managed object is the
 * first element and subsequent elements representing managed objects
 * further down the hierarchy.
 * A path is made up of zero or more elements each of which represents a managed
 * object. Managed objects are arranged hierarchically with the root
 * configuration being the top-most managed object. Elements are ordered such
 * that the root configuration managed object is the first element and
 * subsequent elements representing managed objects further down the hierarchy.
 * <p>
 * A path can be encoded into a string representation using the
 * {@link #toString()} and {@link #toString(StringBuilder)} methods.
 * Conversely, this string representation can be parsed using the
 * {@link #valueOf(String)} method.
 * {@link #toString()} and {@link #toString(StringBuilder)} methods. Conversely,
 * this string representation can be parsed using the {@link #valueOf(String)}
 * method.
 * <p>
 * The string representation of a managed object path is similar in
 * principle to a UNIX file-system path and is defined as follows:
 * The string representation of a managed object path is similar in principle to
 * a UNIX file-system path and is defined as follows:
 * <ul>
 * <li>the root element is represented by the string <code>/</code>
 * <li>subordinate elements are arranged in big-endian order
 * separated by a forward slash <code>/</code> character
 * <li>an element representing a managed object associated with a
 * one-to-one (singleton) or one-to-zero-or-one (optional) relation
 * has the form <code>relation=</code><i>relation</i>
 * <code>[+type=</code><i>definition</i><code>]</code>, where
 * <i>relation</i> is the name of the relation and <i>definition</i>
 * is the name of the referenced managed object's definition if
 * required (usually this is implied by the relation itself)
 * <li>an element representing a managed object associated with a
 * one-to-many (instantiable) relation has the form
 * <code>relation=</code><i>relation</i><code>[+type=</code>
 * <i>definition</i><code>]</code><code>+name=</code><i>name</i>,
 * where <i>relation</i> is the name of the relation and
 * <i>definition</i> is the name of the referenced managed object's
 * definition if required (usually this is implied by the relation
 * itself), and <i>name</i> is the name of the managed object
 * instance
 * <li>an element representing a managed object associated with a
 * one-to-many (set) relation has the form
 * <code>relation=</code><i>relation</i><code>[+type=</code>
 * <i>definition</i><code>]</code>,
 * where <i>relation</i> is the name of the relation and
 * <i>definition</i> is the name of the referenced managed object's
 * definition.
 * <li>subordinate elements are arranged in big-endian order separated by a
 * forward slash <code>/</code> character
 * <li>an element representing a managed object associated with a one-to-one
 * (singleton) or one-to-zero-or-one (optional) relation has the form
 * <code>relation=</code><i>relation</i> <code>[+type=</code><i>definition</i>
 * <code>]</code>, where <i>relation</i> is the name of the relation and
 * <i>definition</i> is the name of the referenced managed object's definition
 * if required (usually this is implied by the relation itself)
 * <li>an element representing a managed object associated with a one-to-many
 * (instantiable) relation has the form <code>relation=</code><i>relation</i>
 * <code>[+type=</code> <i>definition</i><code>]</code><code>+name=</code>
 * <i>name</i>, where <i>relation</i> is the name of the relation and
 * <i>definition</i> is the name of the referenced managed object's definition
 * if required (usually this is implied by the relation itself), and <i>name</i>
 * is the name of the managed object instance
 * <li>an element representing a managed object associated with a one-to-many
 * (set) relation has the form <code>relation=</code><i>relation</i>
 * <code>[+type=</code> <i>definition</i><code>]</code>, where <i>relation</i>
 * is the name of the relation and <i>definition</i> is the name of the
 * referenced managed object's definition.
 * </ul>
 * The following path string representation identifies a connection
 * handler instance (note that the <code>type</code> is not
 * specified indicating that the path identifies a connection handler
 * called <i>my handler</i> which can be any type of connection
 * handler):
 * The following path string representation identifies a connection handler
 * instance (note that the <code>type</code> is not specified indicating that
 * the path identifies a connection handler called <i>my handler</i> which can
 * be any type of connection handler):
 *
 * <pre>
 *  /relation=connection-handler+name=my handler
 * </pre>
 *
 * If the identified connection handler must be an LDAP connection
 * handler then the above path should include the <code>type</code>:
 * If the identified connection handler must be an LDAP connection handler then
 * the above path should include the <code>type</code>:
 *
 * <pre>
 *  /relation=connection-handler+type=ldap-connection-handler+name=my handler
@@ -116,1333 +105,1079 @@
 * </pre>
 *
 * @param <C>
 *          The type of client managed object configuration that this
 *          path references.
 *            The type of client managed object configuration that this path
 *            references.
 * @param <S>
 *          The type of server managed object configuration that this
 *          path references.
 *            The type of server managed object configuration that this path
 *            references.
 */
public final class ManagedObjectPath<C extends ConfigurationClient,
    S extends Configuration> {
  /**
   * A serialize which is used to generate the toDN representation.
   */
  private static final class DNSerializer implements
      ManagedObjectPathSerializer {
    // The current DN.
    private DN dn;
    // The LDAP profile.
    private final LDAPProfile profile;
    // Create a new DN builder.
    private DNSerializer() {
      this.dn = DN.rootDN();
      this.profile = LDAPProfile.getInstance();
    }
public final class ManagedObjectPath<C extends ConfigurationClient, S extends Configuration> {
    /**
     * {@inheritDoc}
     * A serialize which is used to generate the toDN representation.
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement(r);
    private static final class DNSerializer implements ManagedObjectPathSerializer {
      // Now add the single RDN representing the named instance.
      String type = profile.getRelationChildRDNType(r);
      AttributeType atype = DirectoryServer.getAttributeType(
          type.toLowerCase(), true);
      AttributeValue avalue = AttributeValues.create(atype, name);
      dn = dn.concat(RDN.create(atype, avalue));
        // The current DN.
        private DN dn;
        // The LDAP profile.
        private final LDAPProfile profile;
        // Create a new DN builder.
        private DNSerializer() {
            this.dn = DN.rootDN();
            this.profile = LDAPProfile.getInstance();
        }
        /**
         * {@inheritDoc}
         */
        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
                InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d,
                String name) {
            // Add the RDN sequence representing the relation.
            appendManagedObjectPathElement(r);
            // Now add the single RDN representing the named instance.
            String type = profile.getRelationChildRDNType(r);
            AttributeType attrType = DirectoryServer.getAttributeType(type.toLowerCase(), true);
            dn = dn.child(new RDN(attrType, name));
        }
        /**
         * {@inheritDoc}
         */
        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
                SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            // Add the RDN sequence representing the relation.
            appendManagedObjectPathElement(r);
            // Now add the single RDN representing the instance.
            String type = profile.getRelationChildRDNType(r);
            AttributeType attrType = DirectoryServer.getAttributeType(type.toLowerCase(), true);
            dn = dn.child(new RDN(attrType, d.getName()));
        }
        /**
         * {@inheritDoc}
         */
        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
                OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            // Add the RDN sequence representing the relation.
            appendManagedObjectPathElement(r);
        }
        /**
         * {@inheritDoc}
         */
        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
                SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            // Add the RDN sequence representing the relation.
            appendManagedObjectPathElement(r);
        }
        // Appends the RDN sequence representing the provided relation.
        private void appendManagedObjectPathElement(RelationDefinition<?, ?> r) {
            DN localName = DN.valueOf(profile.getRelationRDNSequence(r));
            dn = dn.child(localName);
        }
        // Gets the serialized DN value.
        private DN toDN() {
            return dn;
        }
    }
    /**
     * {@inheritDoc}
     * Abstract path element.
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement(r);
    private static abstract class Element<C extends ConfigurationClient, S extends Configuration> {
      // Now add the single RDN representing the instance.
      String type = profile.getRelationChildRDNType(r);
      AttributeType atype = DirectoryServer.getAttributeType(
          type.toLowerCase(), true);
      AttributeValue avalue = AttributeValues.create(atype, d.getName());
      dn = dn.concat(RDN.create(atype, avalue));
        // The type of managed object referenced by this element.
        private final AbstractManagedObjectDefinition<C, S> definition;
        /**
         * Protected constructor.
         *
         * @param definition
         *            The type of managed object referenced by this element.
         */
        protected Element(AbstractManagedObjectDefinition<C, S> definition) {
            this.definition = definition;
        }
        /**
         * Get the managed object definition associated with this element.
         *
         * @return Returns the managed object definition associated with this
         *         element.
         */
        public final AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
            return definition;
        }
        /**
         * Get the name associated with this element if applicable.
         *
         * @return Returns the name associated with this element if applicable.
         */
        public String getName() {
            return null;
        }
        /**
         * Get the relation definition associated with this element.
         *
         * @return Returns the relation definition associated with this element.
         */
        public abstract RelationDefinition<? super C, ? super S> getRelationDefinition();
        /**
         * Serialize this path element using the provided serialization
         * strategy.
         *
         * @param serializer
         *            The managed object path serialization strategy.
         */
        public abstract void serialize(ManagedObjectPathSerializer serializer);
    }
    /**
     * {@inheritDoc}
     * A path element representing an instantiable managed object.
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement(r);
    private static final class InstantiableElement<C extends ConfigurationClient, S extends Configuration> extends
            Element<C, S> {
        // Factory method.
        private static final <C extends ConfigurationClient, S extends Configuration> InstantiableElement<C, S> create(
                InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d,
                String name) {
            return new InstantiableElement<C, S>(r, d, name);
        }
        // The name of the managed object.
        private final String name;
        // The instantiable relation.
        private final InstantiableRelationDefinition<? super C, ? super S> r;
        // Private constructor.
        private InstantiableElement(InstantiableRelationDefinition<? super C, ? super S> r,
                AbstractManagedObjectDefinition<C, S> d, String name) {
            super(d);
            this.r = r;
            this.name = name;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public String getName() {
            return name;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public InstantiableRelationDefinition<? super C, ? super S> getRelationDefinition() {
            return r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void serialize(ManagedObjectPathSerializer serializer) {
            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition(), name);
        }
    }
    /**
     * {@inheritDoc}
     * A path element representing an optional managed object.
     */
    public <C extends ConfigurationClient, S extends Configuration>
    void appendManagedObjectPathElement(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      // Add the RDN sequence representing the relation.
      appendManagedObjectPathElement(r);
    private static final class OptionalElement<C extends ConfigurationClient, S extends Configuration> extends
            Element<C, S> {
        // Factory method.
        private static final <C extends ConfigurationClient, S extends Configuration> OptionalElement<C, S> create(
                OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            return new OptionalElement<C, S>(r, d);
        }
        // The optional relation.
        private final OptionalRelationDefinition<? super C, ? super S> r;
        // Private constructor.
        private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r,
                AbstractManagedObjectDefinition<C, S> d) {
            super(d);
            this.r = r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public OptionalRelationDefinition<? super C, ? super S> getRelationDefinition() {
            return r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void serialize(ManagedObjectPathSerializer serializer) {
            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
        }
    }
    // Appends the RDN sequence representing the provided relation.
    private void appendManagedObjectPathElement(RelationDefinition<?, ?> r) {
      // Add the RDN sequence representing the relation.
      try {
        DN localName = DN.decode(profile.getRelationRDNSequence(r));
        dn = dn.concat(localName);
      } catch (DirectoryException e) {
        throw new RuntimeException(e);
      }
    }
    // Gets the serialized DN value.
    private DN toDN() {
      return dn;
    }
  }
  /**
   * Abstract path element.
   */
  private static abstract class Element<C extends ConfigurationClient,
      S extends Configuration> {
    // The type of managed object referenced by this element.
    private final AbstractManagedObjectDefinition<C, S> definition;
    /**
     * Protected constructor.
     * A path element representing an set managed object.
     */
    private static final class SetElement<C extends ConfigurationClient, S extends Configuration> extends Element<C, S> {
        // Factory method.
        private static final <C extends ConfigurationClient, S extends Configuration> SetElement<C, S> create(
                SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            return new SetElement<C, S>(r, d);
        }
        // The set relation.
        private final SetRelationDefinition<? super C, ? super S> r;
        // Private constructor.
        private SetElement(SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            super(d);
            this.r = r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public SetRelationDefinition<? super C, ? super S> getRelationDefinition() {
            return r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void serialize(ManagedObjectPathSerializer serializer) {
            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
        }
    }
    /**
     * A path element representing a singleton managed object.
     */
    private static final class SingletonElement<C extends ConfigurationClient, S extends Configuration> extends
            Element<C, S> {
        // Factory method.
        private static final <C extends ConfigurationClient, S extends Configuration> SingletonElement<C, S> create(
                SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
            return new SingletonElement<C, S>(r, d);
        }
        // The singleton relation.
        private final SingletonRelationDefinition<? super C, ? super S> r;
        // Private constructor.
        private SingletonElement(SingletonRelationDefinition<? super C, ? super S> r,
                AbstractManagedObjectDefinition<C, S> d) {
            super(d);
            this.r = r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public SingletonRelationDefinition<? super C, ? super S> getRelationDefinition() {
            return r;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public void serialize(ManagedObjectPathSerializer serializer) {
            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
        }
    }
    /**
     * A serialize which is used to generate the toString representation.
     */
    private static final class StringSerializer implements ManagedObjectPathSerializer {
        // Serialize to this string builder.
        private final StringBuilder builder;
        // Private constructor.
        private StringSerializer(StringBuilder builder) {
            this.builder = builder;
        }
        /**
         * {@inheritDoc}
         */
        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
                InstantiableRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d,
                String name) {
            serializeElement(r, d);
            // Be careful to escape any forward slashes in the name.
            builder.append("+name=");
            builder.append(name.replace("/", "//"));
        }
        /**
         * {@inheritDoc}
         */
        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
                OptionalRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
            serializeElement(r, d);
        }
        /**
         * {@inheritDoc}
         */
        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
                SingletonRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
            serializeElement(r, d);
        }
        /**
         * {@inheritDoc}
         */
        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
                SetRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
            serializeElement(r, d);
        }
        // Common element serialization.
        private <M, N> void serializeElement(RelationDefinition<?, ?> r, AbstractManagedObjectDefinition<?, ?> d) {
            // Always specify the relation name.
            builder.append("/relation=");
            builder.append(r.getName());
            // Only specify the type if it is a sub-type of the relation's
            // type.
            if (r.getChildDefinition() != d) {
                builder.append("+type=");
                builder.append(d.getName());
            }
        }
    }
    // Single instance of a root path.
    private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH = new ManagedObjectPath<RootCfgClient, RootCfg>(
            new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance());
    // A regular expression used to parse path elements.
    private static final Pattern PE_REGEXP = Pattern.compile("^\\s*relation=\\s*([^+]+)\\s*"
            + "(\\+\\s*type=\\s*([^+]+)\\s*)?" + "(\\+\\s*name=\\s*([^+]+)\\s*)?$");
    /**
     * Creates a new managed object path representing the configuration root.
     *
     * @param definition
     *          The type of managed object referenced by this element.
     * @return Returns a new managed object path representing the configuration
     *         root.
     */
    protected Element(AbstractManagedObjectDefinition<C, S> definition) {
      this.definition = definition;
    public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() {
        return EMPTY_PATH;
    }
    /**
     * Get the managed object definition associated with this element.
     * Returns a managed object path holding the value of the specified string.
     *
     * @return Returns the managed object definition associated with
     *         this element.
     * @param s
     *            The string to be parsed.
     * @return Returns a managed object path holding the value of the specified
     *         string.
     * @throws IllegalArgumentException
     *             If the string could not be parsed.
     */
    public final AbstractManagedObjectDefinition<C, S>
        getManagedObjectDefinition() {
      return definition;
    public static ManagedObjectPath<?, ?> valueOf(String s) throws IllegalArgumentException {
        String ns = s.trim();
        // Check for root special case.
        if (ns.equals("/")) {
            return EMPTY_PATH;
        }
        // Parse the elements.
        LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>();
        Element<?, ?> lastElement = null;
        AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn.getInstance();
        if (!ns.startsWith("/")) {
            throw new IllegalArgumentException("Invalid path \"" + ns + "\": must begin with a \"/\"");
        }
        int start = 1;
        while (true) {
            // Get the next path element.
            int end;
            for (end = start; end < ns.length(); end++) {
                char c = ns.charAt(end);
                if (c == '/') {
                    if (end == (ns.length() - 1)) {
                        throw new IllegalArgumentException("Invalid path \"" + ns
                                + "\": must not end with a trailing \"/\"");
                    }
                    if (ns.charAt(end + 1) == '/') {
                        // Found an escaped forward slash.
                        end++;
                    } else {
                        // Found the end of this path element.
                        break;
                    }
                }
            }
            // Get the next element.
            String es = ns.substring(start, end);
            Matcher m = PE_REGEXP.matcher(es);
            if (!m.matches()) {
                throw new IllegalArgumentException("Invalid path element \"" + es + "\" in path \"" + ns + "\"");
            }
            // Mandatory.
            String relation = m.group(1);
            // Optional.
            String type = m.group(3);
            // Mandatory if relation is instantiable.
            String name = m.group(5);
            // Get the relation definition.
            RelationDefinition<?, ?> r;
            try {
                r = definition.getRelationDefinition(relation);
            } catch (IllegalArgumentException e) {
                throw new IllegalArgumentException("Invalid path element \"" + es + "\" in path \"" + ns
                        + "\": unknown relation \"" + relation + "\"");
            }
            // Append the next element.
            lastElement = createElement(r, ns, es, type, name);
            elements.add(lastElement);
            definition = lastElement.getManagedObjectDefinition();
            // Update start to point to the beginning of the next element.
            if (end < ns.length()) {
                // Skip to the beginning of the next element
                start = end + 1;
            } else {
                // We reached the end of the string.
                break;
            }
        }
        // Construct the new path.
        return create(elements, lastElement);
    }
    // Factory method required in order to allow generic wild-card
    // construction of new paths.
    private static <C extends ConfigurationClient, S extends Configuration> ManagedObjectPath<C, S> create(
            LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) {
        return new ManagedObjectPath<C, S>(elements, lastElement.getRelationDefinition(),
                lastElement.getManagedObjectDefinition());
    }
    // Decode an element.
    private static <C extends ConfigurationClient, S extends Configuration> Element<? extends C, ? extends S> createElement(
            RelationDefinition<C, S> r, String path, String element, String type, String name) {
        // First determine the managed object definition.
        AbstractManagedObjectDefinition<? extends C, ? extends S> d = null;
        if (type != null) {
            for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r.getChildDefinition()
                    .getAllChildren()) {
                if (child.getName().equals(type)) {
                    d = child;
                    break;
                }
            }
            if (d == null) {
                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                        + "\": unknown sub-type \"" + type + "\"");
            }
        } else {
            d = r.getChildDefinition();
        }
        if (r instanceof InstantiableRelationDefinition) {
            InstantiableRelationDefinition<C, S> ir = (InstantiableRelationDefinition<C, S>) r;
            if (name == null) {
                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                        + "\": no instance name for instantiable relation");
            }
            return InstantiableElement.create(ir, d, name);
        } else if (r instanceof SetRelationDefinition) {
            SetRelationDefinition<C, S> ir = (SetRelationDefinition<C, S>) r;
            if (name != null) {
                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                        + "\": instance name specified for set relation");
            }
            return SetElement.create(ir, d);
        } else if (r instanceof OptionalRelationDefinition) {
            OptionalRelationDefinition<C, S> or = (OptionalRelationDefinition<C, S>) r;
            if (name != null) {
                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                        + "\": instance name specified for optional relation");
            }
            return OptionalElement.create(or, d);
        } else if (r instanceof SingletonRelationDefinition) {
            SingletonRelationDefinition<C, S> sr = (SingletonRelationDefinition<C, S>) r;
            if (name != null) {
                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                        + "\": instance name specified for singleton relation");
            }
            return SingletonElement.create(sr, d);
        } else {
            throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
                    + "\": unsupported relation type");
        }
    }
    // The managed object definition in this path.
    private final AbstractManagedObjectDefinition<C, S> d;
    // The list of path elements in this path.
    private final List<Element<?, ?>> elements;
    // The last relation definition in this path.
    private final RelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private ManagedObjectPath(LinkedList<Element<?, ?>> elements, RelationDefinition<? super C, ? super S> r,
            AbstractManagedObjectDefinition<C, S> d) {
        this.elements = Collections.unmodifiableList(elements);
        this.r = r;
        this.d = d;
    }
    /**
     * Get the name associated with this element if applicable.
     * Creates a new managed object path which has the same structure as this
     * path except that the final path element is associated with the specified
     * managed object definition.
     *
     * @return Returns the name associated with this element if
     *         applicable.
     * @param <CC>
     *            The type of client managed object configuration that this path
     *            will reference.
     * @param <SS>
     *            The type of server managed object configuration that this path
     *            will reference.
     * @param nd
     *            The new managed object definition.
     * @return Returns a new managed object path which has the same structure as
     *         this path except that the final path element is associated with
     *         the specified managed object definition.
     */
    @SuppressWarnings("unchecked")
    public <CC extends C, SS extends S> ManagedObjectPath<CC, SS> asSubType(AbstractManagedObjectDefinition<CC, SS> nd) {
        if (r instanceof InstantiableRelationDefinition) {
            InstantiableRelationDefinition<? super C, ? super S> ir = (InstantiableRelationDefinition<? super C, ? super S>) r;
            if (elements.size() == 0) {
                return parent().child(ir, nd, "null");
            } else {
                return parent().child(ir, nd, elements.get(elements.size() - 1).getName());
            }
        } else if (r instanceof SetRelationDefinition) {
            SetRelationDefinition<? super C, ? super S> sr = (SetRelationDefinition<? super C, ? super S>) r;
            return parent().child(sr, nd);
        } else if (r instanceof OptionalRelationDefinition) {
            OptionalRelationDefinition<? super C, ? super S> or = (OptionalRelationDefinition<? super C, ? super S>) r;
            return parent().child(or, nd);
        } else {
            SingletonRelationDefinition<? super C, ? super S> sr = (SingletonRelationDefinition<? super C, ? super S>) r;
            return parent().child(sr, nd);
        }
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * having the specified managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The instantiable relation referencing the child.
     * @param d
     *            The managed object definition associated with the child (must
     *            be a sub-type of the relation).
     * @param name
     *            The relative name of the child managed object.
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     * @throws IllegalArgumentException
     *             If the provided name is empty or blank.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            InstantiableRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d, String name)
            throws IllegalArgumentException {
        if (name.trim().length() == 0) {
            throw new IllegalArgumentException("Empty or blank managed object names are not allowed");
        }
        LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(elements);
        celements.add(new InstantiableElement<M, N>(r, d, name));
        return new ManagedObjectPath<M, N>(celements, r, d);
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * using the relation's child managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The instantiable relation referencing the child.
     * @param name
     *            The relative name of the child managed object.
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     * @throws IllegalArgumentException
     *             If the provided name is empty or blank.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            InstantiableRelationDefinition<M, N> r, String name) throws IllegalArgumentException {
        return child(r, r.getChildDefinition(), name);
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * having the specified managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The optional relation referencing the child.
     * @param d
     *            The managed object definition associated with the child (must
     *            be a sub-type of the relation).
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            OptionalRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
        LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(elements);
        celements.add(new OptionalElement<M, N>(r, d));
        return new ManagedObjectPath<M, N>(celements, r, d);
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * using the relation's child managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The optional relation referencing the child.
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            OptionalRelationDefinition<M, N> r) {
        return child(r, r.getChildDefinition());
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * having the specified managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The singleton relation referencing the child.
     * @param d
     *            The managed object definition associated with the child (must
     *            be a sub-type of the relation).
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            SingletonRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
        LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(elements);
        celements.add(new SingletonElement<M, N>(r, d));
        return new ManagedObjectPath<M, N>(celements, r, d);
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * using the relation's child managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The singleton relation referencing the child.
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            SingletonRelationDefinition<M, N> r) {
        return child(r, r.getChildDefinition());
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * having the specified managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The set relation referencing the child.
     * @param d
     *            The managed object definition associated with the child (must
     *            be a sub-type of the relation).
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     * @throws IllegalArgumentException
     *             If the provided name is empty or blank.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            SetRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d)
            throws IllegalArgumentException {
        LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(elements);
        celements.add(new SetElement<M, N>(r, d));
        return new ManagedObjectPath<M, N>(celements, r, d);
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * having the managed object definition indicated by <code>name</code>.
     *
     * @param <M>
     *            The type of client managed object configuration that the path
     *            references.
     * @param <N>
     *            The type of server managed object configuration that the path
     *            references.
     * @param r
     *            The set relation referencing the child.
     * @param name
     *            The name of the managed object definition associated with the
     *            child (must be a sub-type of the relation).
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     * @throws IllegalArgumentException
     *             If the provided name is empty or blank or specifies a managed
     *             object definition which is not a sub-type of the relation's
     *             child definition.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<? extends M, ? extends N> child(
            SetRelationDefinition<M, N> r, String name) throws IllegalArgumentException {
        AbstractManagedObjectDefinition<M, N> d = r.getChildDefinition();
        return child(r, d.getChild(name));
    }
    /**
     * Creates a new child managed object path beneath the provided parent path
     * using the relation's child managed object definition.
     *
     * @param <M>
     *            The type of client managed object configuration that the child
     *            path references.
     * @param <N>
     *            The type of server managed object configuration that the child
     *            path references.
     * @param r
     *            The set relation referencing the child.
     * @return Returns a new child managed object path beneath the provided
     *         parent path.
     * @throws IllegalArgumentException
     *             If the provided name is empty or blank.
     */
    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
            SetRelationDefinition<M, N> r) throws IllegalArgumentException {
        return child(r, r.getChildDefinition());
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean equals(Object obj) {
        if (obj == this) {
            return true;
        } else if (obj instanceof ManagedObjectPath) {
            ManagedObjectPath<?, ?> other = (ManagedObjectPath<?, ?>) obj;
            return toString().equals(other.toString());
        } else {
            return false;
        }
    }
    /**
     * Get the definition of the managed object referred to by this path.
     * <p>
     * When the path is empty, the {@link RootCfgDefn} is returned.
     *
     * @return Returns the definition of the managed object referred to by this
     *         path, or the {@link RootCfgDefn} if the path is empty.
     */
    public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
        return d;
    }
    /**
     * Get the name of the managed object referred to by this path if
     * applicable.
     * <p>
     * If there path does not refer to an instantiable managed object
     * <code>null</code> is returned.
     *
     * @return Returns the name of the managed object referred to by this path,
     *         or <code>null</code> if the managed object does not have a name.
     */
    public String getName() {
      return null;
        if (elements.isEmpty()) {
            return null;
        } else {
            return elements.get(elements.size() - 1).getName();
        }
    }
    /**
     * Get the relation definition associated with this element.
     * Get the relation definition of the managed object referred to by this
     * path.
     * <p>
     * When the path is empty, the <code>null</code> is returned.
     *
     * @return Returns the relation definition associated with this
     *         element.
     * @return Returns the relation definition of the managed object referred to
     *         by this path, or the <code>null</code> if the path is empty.
     */
    public abstract RelationDefinition<? super C, ? super S>
        getRelationDefinition();
    public RelationDefinition<? super C, ? super S> getRelationDefinition() {
        return r;
    }
    /**
     * Serialize this path element using the provided serialization
     * {@inheritDoc}
     */
    @Override
    public int hashCode() {
        return toString().hashCode();
    }
    /**
     * Determine whether or not this path contains any path elements.
     *
     * @return Returns <code>true</code> if this path does not contain any path
     *         elements.
     */
    public boolean isEmpty() {
        return elements.isEmpty();
    }
    /**
     * Determines whether this managed object path references the same location
     * as the provided managed object path.
     * <p>
     * This method differs from <code>equals</code> in that it ignores sub-type
     * definitions.
     *
     * @param other
     *            The managed object path to be compared.
     * @return Returns <code>true</code> if this managed object path references
     *         the same location as the provided managed object path.
     */
    public boolean matches(ManagedObjectPath<?, ?> other) {
        DN thisDN = toDN();
        DN otherDN = other.toDN();
        return thisDN.equals(otherDN);
    }
    /**
     * Creates a new parent managed object path representing the immediate
     * parent of this path. This method is a short-hand for
     * <code>parent(1)</code>.
     *
     * @return Returns a new parent managed object path representing the
     *         immediate parent of this path.
     * @throws IllegalArgumentException
     *             If this path does not have a parent (i.e. it is the empty
     *             path).
     */
    public ManagedObjectPath<?, ?> parent() throws IllegalArgumentException {
        return parent(1);
    }
    /**
     * Creates a new parent managed object path the specified number of path
     * elements above this path.
     *
     * @param offset
     *            The number of path elements (0 - means no offset, 1 means the
     *            parent, and 2 means the grand-parent).
     * @return Returns a new parent managed object path the specified number of
     *         path elements above this path.
     * @throws IllegalArgumentException
     *             If the offset is less than 0, or greater than the number of
     *             path elements in this path.
     */
    public ManagedObjectPath<?, ?> parent(int offset) throws IllegalArgumentException {
        if (offset < 0) {
            throw new IllegalArgumentException("Negative offset");
        }
        if (offset > elements.size()) {
            throw new IllegalArgumentException("Offset is greater than the number of path elements");
        }
        // An offset of 0 leaves the path unchanged.
        if (offset == 0) {
            return this;
        }
        // Return the empty path if the parent has zero elements.
        if (elements.size() == offset) {
            return emptyPath();
        }
        LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(elements.subList(0, elements.size()
                - offset));
        return create(celements, celements.getLast());
    }
    /**
     * Creates a new managed object path which has the same structure as this
     * path except that the final path element is renamed. The final path
     * element must comprise of an instantiable relation.
     *
     * @param newName
     *            The new name of the final path element.
     * @return Returns a new managed object path which has the same structure as
     *         this path except that the final path element is renamed.
     * @throws IllegalStateException
     *             If this managed object path is empty or if its final path
     *             element does not comprise of an instantiable relation.
     */
    @SuppressWarnings("unchecked")
    public ManagedObjectPath<C, S> rename(String newName) throws IllegalStateException {
        if (elements.size() == 0) {
            throw new IllegalStateException("Cannot rename an empty path");
        }
        if (r instanceof InstantiableRelationDefinition) {
            InstantiableRelationDefinition<? super C, ? super S> ir = (InstantiableRelationDefinition<? super C, ? super S>) r;
            return parent().child(ir, d, newName);
        } else {
            throw new IllegalStateException("Not an instantiable relation");
        }
    }
    /**
     * Serialize this managed object path using the provided serialization
     * strategy.
     * <p>
     * The path elements will be passed to the serializer in big-endian order:
     * starting from the root element and proceeding down to the leaf.
     *
     * @param serializer
     *          The managed object path serialization strategy.
     *            The managed object path serialization strategy.
     */
    public abstract void serialize(ManagedObjectPathSerializer serializer);
  }
  /**
   * A path element representing an instantiable managed object.
   */
  private static final class InstantiableElement
      <C extends ConfigurationClient, S extends Configuration>
      extends Element<C, S> {
    // Factory method.
    private static final <C extends ConfigurationClient,
        S extends Configuration>
        InstantiableElement<C, S> create(
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
      return new InstantiableElement<C, S>(r, d, name);
    }
    // The name of the managed object.
    private final String name;
    // The instantiable relation.
    private final InstantiableRelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private InstantiableElement(
        InstantiableRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d, String name) {
      super(d);
      this.r = r;
      this.name = name;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getName() {
      return name;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public InstantiableRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer.appendManagedObjectPathElement(r,
          getManagedObjectDefinition(), name);
    }
  }
  /**
   * A path element representing an optional managed object.
   */
  private static final class OptionalElement
      <C extends ConfigurationClient, S extends Configuration>
      extends Element<C, S> {
    // Factory method.
    private static final <C extends ConfigurationClient,
        S extends Configuration> OptionalElement<C, S> create(
        OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new OptionalElement<C, S>(r, d);
    }
    // The optional relation.
    private final OptionalRelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      super(d);
      this.r = r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public OptionalRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer
          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
    }
  }
  /**
   * A path element representing an set managed object.
   */
  private static final class SetElement
      <C extends ConfigurationClient, S extends Configuration>
      extends Element<C, S> {
    // Factory method.
    private static final <C extends ConfigurationClient,
        S extends Configuration>
        SetElement<C, S> create(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new SetElement<C, S>(r, d);
    }
    // The set relation.
    private final SetRelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private SetElement(
        SetRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      super(d);
      this.r = r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SetRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer.appendManagedObjectPathElement(r,
          getManagedObjectDefinition());
    }
  }
  /**
   * A path element representing a singleton managed object.
   */
  private static final class SingletonElement
      <C extends ConfigurationClient, S extends Configuration>
      extends Element<C, S> {
    // Factory method.
    private static final <C extends ConfigurationClient,
        S extends Configuration> SingletonElement<C, S> create(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      return new SingletonElement<C, S>(r, d);
    }
    // The singleton relation.
    private final SingletonRelationDefinition<? super C, ? super S> r;
    // Private constructor.
    private SingletonElement(
        SingletonRelationDefinition<? super C, ? super S> r,
        AbstractManagedObjectDefinition<C, S> d) {
      super(d);
      this.r = r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SingletonRelationDefinition<? super C, ? super S>
        getRelationDefinition() {
      return r;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public void serialize(ManagedObjectPathSerializer serializer) {
      serializer
          .appendManagedObjectPathElement(r, getManagedObjectDefinition());
    }
  }
  /**
   * A serialize which is used to generate the toString
   * representation.
   */
  private static final class StringSerializer implements
      ManagedObjectPathSerializer {
    // Serialize to this string builder.
    private final StringBuilder builder;
    // Private constructor.
    private StringSerializer(StringBuilder builder) {
      this.builder = builder;
    }
    /**
     * {@inheritDoc}
     */
    public <M extends ConfigurationClient, N extends Configuration>
        void appendManagedObjectPathElement(
        InstantiableRelationDefinition<? super M, ? super N> r,
        AbstractManagedObjectDefinition<M, N> d, String name) {
      serializeElement(r, d);
      // Be careful to escape any forward slashes in the name.
      builder.append("+name=");
      builder.append(name.replace("/", "//"));
    }
    /**
     * {@inheritDoc}
     */
    public <M extends ConfigurationClient, N extends Configuration>
        void appendManagedObjectPathElement(
        OptionalRelationDefinition<? super M, ? super N> r,
        AbstractManagedObjectDefinition<M, N> d) {
      serializeElement(r, d);
    }
    /**
     * {@inheritDoc}
     */
    public <M extends ConfigurationClient, N extends Configuration>
        void appendManagedObjectPathElement(
        SingletonRelationDefinition<? super M, ? super N> r,
        AbstractManagedObjectDefinition<M, N> d) {
      serializeElement(r, d);
    }
    /**
     * {@inheritDoc}
     */
    public <M extends ConfigurationClient, N extends Configuration>
        void appendManagedObjectPathElement(
        SetRelationDefinition<? super M, ? super N> r,
        AbstractManagedObjectDefinition<M, N> d) {
      serializeElement(r, d);
    }
    // Common element serialization.
    private <M, N> void serializeElement(RelationDefinition<?, ?> r,
        AbstractManagedObjectDefinition<?, ?> d) {
      // Always specify the relation name.
      builder.append("/relation=");
      builder.append(r.getName());
      // Only specify the type if it is a sub-type of the relation's
      // type.
      if (r.getChildDefinition() != d) {
        builder.append("+type=");
        builder.append(d.getName());
      }
    }
  }
  // Single instance of a root path.
  private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH =
      new ManagedObjectPath<RootCfgClient, RootCfg>(
      new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance());
  // A regular expression used to parse path elements.
  private static final Pattern PE_REGEXP = Pattern
      .compile("^\\s*relation=\\s*([^+]+)\\s*"
          + "(\\+\\s*type=\\s*([^+]+)\\s*)?"
          + "(\\+\\s*name=\\s*([^+]+)\\s*)?$");
  /**
   * Creates a new managed object path representing the configuration
   * root.
   *
   * @return Returns a new managed object path representing the
   *         configuration root.
   */
  public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() {
    return EMPTY_PATH;
  }
  /**
   * Returns a managed object path holding the value of the specified
   * string.
   *
   * @param s
   *          The string to be parsed.
   * @return Returns a managed object path holding the value of the
   *         specified string.
   * @throws IllegalArgumentException
   *           If the string could not be parsed.
   */
  public static ManagedObjectPath<?, ?> valueOf(String s)
      throws IllegalArgumentException {
    String ns = s.trim();
    // Check for root special case.
    if (ns.equals("/")) {
      return EMPTY_PATH;
    }
    // Parse the elements.
    LinkedList<Element<?, ?>> elements = new LinkedList<Element<?, ?>>();
    Element<?, ?> lastElement = null;
    AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn
        .getInstance();
    if (!ns.startsWith("/")) {
      throw new IllegalArgumentException("Invalid path \"" + ns
          + "\": must begin with a \"/\"");
    }
    int start = 1;
    while (true) {
      // Get the next path element.
      int end;
      for (end = start; end < ns.length(); end++) {
        char c = ns.charAt(end);
        if (c == '/') {
          if (end == (ns.length() - 1)) {
            throw new IllegalArgumentException("Invalid path \"" + ns
                + "\": must not end with a trailing \"/\"");
          }
          if (ns.charAt(end + 1) == '/') {
            // Found an escaped forward slash.
            end++;
          } else {
            // Found the end of this path element.
            break;
          }
        for (Element<?, ?> element : elements) {
            element.serialize(serializer);
        }
      }
      // Get the next element.
      String es = ns.substring(start, end);
      Matcher m = PE_REGEXP.matcher(es);
      if (!m.matches()) {
        throw new IllegalArgumentException("Invalid path element \"" + es
            + "\" in path \"" + ns + "\"");
      }
      // Mandatory.
      String relation = m.group(1);
      // Optional.
      String type = m.group(3);
      // Mandatory if relation is instantiable.
      String name = m.group(5);
      // Get the relation definition.
      RelationDefinition<?, ?> r;
      try {
        r = definition.getRelationDefinition(relation);
      } catch (IllegalArgumentException e) {
        throw new IllegalArgumentException("Invalid path element \"" + es
            + "\" in path \"" + ns + "\": unknown relation \"" + relation
            + "\"");
      }
      // Append the next element.
      lastElement = createElement(r, ns, es, type, name);
      elements.add(lastElement);
      definition = lastElement.getManagedObjectDefinition();
      // Update start to point to the beginning of the next element.
      if (end < ns.length()) {
        // Skip to the beginning of the next element
        start = end + 1;
      } else {
        // We reached the end of the string.
        break;
      }
    }
    // Construct the new path.
    return create(elements, lastElement);
  }
    /**
     * Get the number of path elements in this managed object path.
     *
     * @return Returns the number of path elements (0 - means no offset, 1 means
     *         the parent, and 2 means the grand-parent).
     */
    public int size() {
        return elements.size();
    }
    /**
     * Creates a DN representation of this managed object path.
     *
     * @return Returns a DN representation of this managed object path.
     */
    public DN toDN() {
        // Use a simple serializer to create the contents.
        DNSerializer serializer = new DNSerializer();
        serialize(serializer);
        return serializer.toDN();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        toString(builder);
        return builder.toString();
    }
  // Factory method required in order to allow generic wild-card
  // construction of new paths.
  private static <C extends ConfigurationClient, S extends Configuration>
      ManagedObjectPath<C, S> create(
      LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) {
    return new ManagedObjectPath<C, S>(elements, lastElement
        .getRelationDefinition(), lastElement.getManagedObjectDefinition());
  }
  // Decode an element.
  private static <C extends ConfigurationClient, S extends Configuration>
      Element<? extends C, ? extends S> createElement(
      RelationDefinition<C, S> r, String path, String element, String type,
      String name) {
    // First determine the managed object definition.
    AbstractManagedObjectDefinition<? extends C, ? extends S> d = null;
    if (type != null) {
      for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r
          .getChildDefinition().getAllChildren()) {
        if (child.getName().equals(type)) {
          d = child;
          break;
    /**
     * Appends a string representation of this managed object path to the
     * provided string builder.
     *
     * @param builder
     *            Append the string representation to this builder.
     * @see #toString()
     */
    public void toString(final StringBuilder builder) {
        if (isEmpty()) {
            // Special treatment of root configuration paths.
            builder.append('/');
        } else {
            // Use a simple serializer to create the contents.
            ManagedObjectPathSerializer serializer = new StringSerializer(builder);
            serialize(serializer);
        }
      }
      if (d == null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path + "\": unknown sub-type \"" + type + "\"");
      }
    } else {
      d = r.getChildDefinition();
    }
    if (r instanceof InstantiableRelationDefinition) {
      InstantiableRelationDefinition<C, S> ir =
        (InstantiableRelationDefinition<C, S>) r;
      if (name == null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path
            + "\": no instance name for instantiable relation");
      }
      return InstantiableElement.create(ir, d, name);
    } else if (r instanceof SetRelationDefinition) {
      SetRelationDefinition<C, S> ir = (SetRelationDefinition<C, S>) r;
      if (name != null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path
            + "\": instance name specified for set relation");
      }
      return SetElement.create(ir, d);
    } else if (r instanceof OptionalRelationDefinition) {
      OptionalRelationDefinition<C, S> or =
        (OptionalRelationDefinition<C, S>) r;
      if (name != null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path
            + "\": instance name specified for optional relation");
      }
      return OptionalElement.create(or, d);
    } else if (r instanceof SingletonRelationDefinition) {
      SingletonRelationDefinition<C, S> sr =
        (SingletonRelationDefinition<C, S>) r;
      if (name != null) {
        throw new IllegalArgumentException("Invalid path element \"" + element
            + "\" in path \"" + path
            + "\": instance name specified for singleton relation");
      }
      return SingletonElement.create(sr, d);
    } else {
      throw new IllegalArgumentException("Invalid path element \"" + element
          + "\" in path \"" + path + "\": unsupported relation type");
    }
  }
  // The managed object definition in this path.
  private final AbstractManagedObjectDefinition<C, S> d;
  // The list of path elements in this path.
  private final List<Element<?, ?>> elements;
  // The last relation definition in this path.
  private final RelationDefinition<? super C, ? super S> r;
  // Private constructor.
  private ManagedObjectPath(LinkedList<Element<?, ?>> elements,
      RelationDefinition<? super C, ? super S> r,
      AbstractManagedObjectDefinition<C, S> d) {
    this.elements = Collections.unmodifiableList(elements);
    this.r = r;
    this.d = d;
  }
  /**
   * Creates a new managed object path which has the same structure as
   * this path except that the final path element is associated with
   * the specified managed object definition.
   *
   * @param <CC>
   *          The type of client managed object configuration that
   *          this path will reference.
   * @param <SS>
   *          The type of server managed object configuration that
   *          this path will reference.
   * @param nd
   *          The new managed object definition.
   * @return Returns a new managed object path which has the same
   *         structure as this path except that the final path element
   *         is associated with the specified managed object
   *         definition.
   */
  @SuppressWarnings("unchecked")
  public <CC extends C, SS extends S> ManagedObjectPath<CC, SS> asSubType(
      AbstractManagedObjectDefinition<CC, SS> nd) {
    if (r instanceof InstantiableRelationDefinition) {
      InstantiableRelationDefinition<? super C, ? super S> ir =
        (InstantiableRelationDefinition<? super C, ? super S>) r;
      if (elements.size() == 0) {
        return parent().child(ir, nd, "null");
      } else {
        return parent().child(ir, nd,
            elements.get(elements.size() - 1).getName());
      }
    } else if (r instanceof SetRelationDefinition) {
      SetRelationDefinition<? super C, ? super S> sr =
        (SetRelationDefinition<? super C, ? super S>) r;
      return parent().child(sr, nd);
    } else if (r instanceof OptionalRelationDefinition) {
      OptionalRelationDefinition<? super C, ? super S> or =
        (OptionalRelationDefinition<? super C, ? super S>) r;
      return parent().child(or, nd);
    } else {
      SingletonRelationDefinition<? super C, ? super S> sr =
        (SingletonRelationDefinition<? super C, ? super S>) r;
      return parent().child(sr, nd);
    }
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path having the specified managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The instantiable relation referencing the child.
   * @param d
   *          The managed object definition associated with the child
   *          (must be a sub-type of the relation).
   * @param name
   *          The relative name of the child managed object.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      InstantiableRelationDefinition<? super M, ? super N> r,
      AbstractManagedObjectDefinition<M, N> d, String name)
      throws IllegalArgumentException {
    if (name.trim().length() == 0) {
      throw new IllegalArgumentException(
          "Empty or blank managed object names are not allowed");
    }
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new InstantiableElement<M, N>(r, d, name));
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path using the relation's child managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The instantiable relation referencing the child.
   * @param name
   *          The relative name of the child managed object.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      InstantiableRelationDefinition<M, N> r, String name)
      throws IllegalArgumentException {
    return child(r, r.getChildDefinition(), name);
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path having the specified managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The optional relation referencing the child.
   * @param d
   *          The managed object definition associated with the child
   *          (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      OptionalRelationDefinition<? super M, ? super N> r,
      AbstractManagedObjectDefinition<M, N> d) {
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new OptionalElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path using the relation's child managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The optional relation referencing the child.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(OptionalRelationDefinition<M, N> r) {
    return child(r, r.getChildDefinition());
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path having the specified managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The singleton relation referencing the child.
   * @param d
   *          The managed object definition associated with the child
   *          (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SingletonRelationDefinition<? super M, ? super N> r,
      AbstractManagedObjectDefinition<M, N> d) {
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new SingletonElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path using the relation's child managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The singleton relation referencing the child.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(SingletonRelationDefinition<M, N> r) {
    return child(r, r.getChildDefinition());
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path having the specified managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The set relation referencing the child.
   * @param d
   *          The managed object definition associated with the child
   *          (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SetRelationDefinition<? super M, ? super N> r,
      AbstractManagedObjectDefinition<M, N> d)
      throws IllegalArgumentException {
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements);
    celements.add(new SetElement<M, N>(r, d));
    return new ManagedObjectPath<M, N>(celements, r, d);
  }
  /**
   * Creates a new child managed object path beneath the provided parent
   * path having the managed object definition indicated by
   * <code>name</code>.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          path references.
   * @param r
   *          The set relation referencing the child.
   * @param name
   *          The name of the managed object definition associated with
   *          the child (must be a sub-type of the relation).
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank or specifies a
   *           managed object definition which is not a sub-type of the
   *           relation's child definition.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<? extends M, ? extends N> child(
      SetRelationDefinition<M, N> r,
      String name)
      throws IllegalArgumentException {
    AbstractManagedObjectDefinition<M, N> d = r.getChildDefinition();
    return child(r, d.getChild(name));
  }
  /**
   * Creates a new child managed object path beneath the provided
   * parent path using the relation's child managed object definition.
   *
   * @param <M>
   *          The type of client managed object configuration that the
   *          child path references.
   * @param <N>
   *          The type of server managed object configuration that the
   *          child path references.
   * @param r
   *          The set relation referencing the child.
   * @return Returns a new child managed object path beneath the
   *         provided parent path.
   * @throws IllegalArgumentException
   *           If the provided name is empty or blank.
   */
  public <M extends ConfigurationClient, N extends Configuration>
      ManagedObjectPath<M, N> child(
      SetRelationDefinition<M, N> r)
      throws IllegalArgumentException {
    return child(r, r.getChildDefinition());
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public boolean equals(Object obj) {
    if (obj == this) {
      return true;
    } else if (obj instanceof ManagedObjectPath) {
      ManagedObjectPath<?, ?> other = (ManagedObjectPath<?, ?>) obj;
      return toString().equals(other.toString());
    } else {
      return false;
    }
  }
  /**
   * Get the definition of the managed object referred to by this
   * path.
   * <p>
   * When the path is empty, the {@link RootCfgDefn} is returned.
   *
   * @return Returns the definition of the managed object referred to
   *         by this path, or the {@link RootCfgDefn} if the path is
   *         empty.
   */
  public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
    return d;
  }
  /**
   * Get the name of the managed object referred to by this path if
   * applicable.
   * <p>
   * If there path does not refer to an instantiable managed object
   * <code>null</code> is returned.
   *
   * @return Returns the name of the managed object referred to by
   *         this path, or <code>null</code> if the managed object
   *         does not have a name.
   */
  public String getName() {
    if (elements.isEmpty()) {
      return null;
    } else {
      return elements.get(elements.size() - 1).getName();
    }
  }
  /**
   * Get the relation definition of the managed object referred to by
   * this path.
   * <p>
   * When the path is empty, the <code>null</code> is returned.
   *
   * @return Returns the relation definition of the managed object
   *         referred to by this path, or the <code>null</code> if
   *         the path is empty.
   */
  public RelationDefinition<? super C, ? super S> getRelationDefinition() {
    return r;
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public int hashCode() {
    return toString().hashCode();
  }
  /**
   * Determine whether or not this path contains any path elements.
   *
   * @return Returns <code>true</code> if this path does not contain
   *         any path elements.
   */
  public boolean isEmpty() {
    return elements.isEmpty();
  }
  /**
   * Determines whether this managed object path references the same
   * location as the provided managed object path.
   * <p>
   * This method differs from <code>equals</code> in that it ignores
   * sub-type definitions.
   *
   * @param other
   *          The managed object path to be compared.
   * @return Returns <code>true</code> if this managed object path
   *         references the same location as the provided managed
   *         object path.
   */
  public boolean matches(ManagedObjectPath<?, ?> other) {
    DN thisDN = toDN();
    DN otherDN = other.toDN();
    return thisDN.equals(otherDN);
  }
  /**
   * Creates a new parent managed object path representing the
   * immediate parent of this path. This method is a short-hand for
   * <code>parent(1)</code>.
   *
   * @return Returns a new parent managed object path representing the
   *         immediate parent of this path.
   * @throws IllegalArgumentException
   *           If this path does not have a parent (i.e. it is the
   *           empty path).
   */
  public ManagedObjectPath<?, ?> parent() throws IllegalArgumentException {
    return parent(1);
  }
  /**
   * Creates a new parent managed object path the specified number of
   * path elements above this path.
   *
   * @param offset
   *          The number of path elements (0 - means no offset, 1
   *          means the parent, and 2 means the grand-parent).
   * @return Returns a new parent managed object path the specified
   *         number of path elements above this path.
   * @throws IllegalArgumentException
   *           If the offset is less than 0, or greater than the
   *           number of path elements in this path.
   */
  public ManagedObjectPath<?, ?> parent(int offset)
      throws IllegalArgumentException {
    if (offset < 0) {
      throw new IllegalArgumentException("Negative offset");
    }
    if (offset > elements.size()) {
      throw new IllegalArgumentException(
          "Offset is greater than the number of path elements");
    }
    // An offset of 0 leaves the path unchanged.
    if (offset == 0) {
      return this;
    }
    // Return the empty path if the parent has zero elements.
    if (elements.size() == offset) {
      return emptyPath();
    }
    LinkedList<Element<?, ?>> celements = new LinkedList<Element<?, ?>>(
        elements.subList(0, elements.size() - offset));
    return create(celements, celements.getLast());
  }
  /**
   * Creates a new managed object path which has the same structure as
   * this path except that the final path element is renamed. The
   * final path element must comprise of an instantiable relation.
   *
   * @param newName
   *          The new name of the final path element.
   * @return Returns a new managed object path which has the same
   *         structure as this path except that the final path element
   *         is renamed.
   * @throws IllegalStateException
   *           If this managed object path is empty or if its final
   *           path element does not comprise of an instantiable
   *           relation.
   */
  @SuppressWarnings("unchecked")
  public ManagedObjectPath<C, S> rename(String newName)
      throws IllegalStateException {
    if (elements.size() == 0) {
      throw new IllegalStateException("Cannot rename an empty path");
    }
    if (r instanceof InstantiableRelationDefinition) {
      InstantiableRelationDefinition<? super C, ? super S> ir =
        (InstantiableRelationDefinition<? super C, ? super S>) r;
      return parent().child(ir, d, newName);
    } else {
      throw new IllegalStateException("Not an instantiable relation");
    }
  }
  /**
   * Serialize this managed object path using the provided
   * serialization strategy.
   * <p>
   * The path elements will be passed to the serializer in big-endian
   * order: starting from the root element and proceeding down to the
   * leaf.
   *
   * @param serializer
   *          The managed object path serialization strategy.
   */
  public void serialize(ManagedObjectPathSerializer serializer) {
    for (Element<?, ?> element : elements) {
      element.serialize(serializer);
    }
  }
  /**
   * Get the number of path elements in this managed object path.
   *
   * @return Returns the number of path elements (0 - means no offset,
   *         1 means the parent, and 2 means the grand-parent).
   */
  public int size() {
    return elements.size();
  }
  /**
   * Creates a DN representation of this managed object path.
   *
   * @return Returns a DN representation of this managed object path.
   */
  public DN toDN() {
    // Use a simple serializer to create the contents.
    DNSerializer serializer = new DNSerializer();
    serialize(serializer);
    return serializer.toDN();
  }
  /**
   * {@inheritDoc}
   */
  @Override
  public String toString() {
    StringBuilder builder = new StringBuilder();
    toString(builder);
    return builder.toString();
  }
  /**
   * Appends a string representation of this managed object path to
   * the provided string builder.
   *
   * @param builder
   *          Append the string representation to this builder.
   * @see #toString()
   */
  public void toString(final StringBuilder builder) {
    if (isEmpty()) {
      // Special treatment of root configuration paths.
      builder.append('/');
    } else {
      // Use a simple serializer to create the contents.
      ManagedObjectPathSerializer serializer = new StringSerializer(builder);
      serialize(serializer);
    }
  }
}