001/*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License").  You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at legal-notices/CDDLv1_0.txt
010 * or http://forgerock.org/license/CDDLv1.0.html.
011 * See the License for the specific language governing permissions
012 * and limitations under the License.
013 *
014 * When distributing Covered Code, include this CDDL HEADER in each
015 * file and include the License file at legal-notices/CDDLv1_0.txt.
016 * If applicable, add the following below this CDDL HEADER, with the
017 * fields enclosed by brackets "[]" replaced with your own identifying
018 * information:
019 *      Portions Copyright [yyyy] [name of copyright owner]
020 *
021 * CDDL HEADER END
022 *
023 *
024 *      Copyright 2008-2009 Sun Microsystems, Inc.
025 *      Portions Copyright 2011-2015 ForgeRock AS
026 */
027
028package org.forgerock.opendj.config;
029
030import java.util.Collections;
031import java.util.LinkedList;
032import java.util.List;
033import java.util.regex.Matcher;
034import java.util.regex.Pattern;
035
036import org.forgerock.opendj.server.config.client.RootCfgClient;
037import org.forgerock.opendj.server.config.meta.RootCfgDefn;
038import org.forgerock.opendj.server.config.server.RootCfg;
039import org.forgerock.opendj.ldap.DN;
040import org.forgerock.opendj.ldap.RDN;
041import org.forgerock.opendj.ldap.schema.AttributeType;
042import org.forgerock.opendj.ldap.schema.Schema;
043
044/**
045 * A path which can be used to determine the location of a managed object
046 * instance.
047 * <p>
048 * A path is made up of zero or more elements each of which represents a managed
049 * object. Managed objects are arranged hierarchically with the root
050 * configuration being the top-most managed object. Elements are ordered such
051 * that the root configuration managed object is the first element and
052 * subsequent elements representing managed objects further down the hierarchy.
053 * <p>
054 * A path can be encoded into a string representation using the
055 * {@link #toString()} and {@link #toString(StringBuilder)} methods. Conversely,
056 * this string representation can be parsed using the {@link #valueOf(String)}
057 * method.
058 * <p>
059 * The string representation of a managed object path is similar in principle to
060 * a UNIX file-system path and is defined as follows:
061 * <ul>
062 * <li>the root element is represented by the string <code>/</code>
063 * <li>subordinate elements are arranged in big-endian order separated by a
064 * forward slash <code>/</code> character
065 * <li>an element representing a managed object associated with a one-to-one
066 * (singleton) or one-to-zero-or-one (optional) relation has the form
067 * <code>relation=</code><i>relation</i> <code>[+type=</code><i>definition</i>
068 * <code>]</code>, where <i>relation</i> is the name of the relation and
069 * <i>definition</i> is the name of the referenced managed object's definition
070 * if required (usually this is implied by the relation itself)
071 * <li>an element representing a managed object associated with a one-to-many
072 * (instantiable) relation has the form <code>relation=</code><i>relation</i>
073 * <code>[+type=</code> <i>definition</i><code>]</code><code>+name=</code>
074 * <i>name</i>, where <i>relation</i> is the name of the relation and
075 * <i>definition</i> is the name of the referenced managed object's definition
076 * if required (usually this is implied by the relation itself), and <i>name</i>
077 * is the name of the managed object instance
078 * <li>an element representing a managed object associated with a one-to-many
079 * (set) relation has the form <code>relation=</code><i>relation</i>
080 * <code>[+type=</code> <i>definition</i><code>]</code>, where <i>relation</i>
081 * is the name of the relation and <i>definition</i> is the name of the
082 * referenced managed object's definition.
083 * </ul>
084 * The following path string representation identifies a connection handler
085 * instance (note that the <code>type</code> is not specified indicating that
086 * the path identifies a connection handler called <i>my handler</i> which can
087 * be any type of connection handler):
088 *
089 * <pre>
090 *  /relation=connection-handler+name=my handler
091 * </pre>
092 *
093 * If the identified connection handler must be an LDAP connection handler then
094 * the above path should include the <code>type</code>:
095 *
096 * <pre>
097 *  /relation=connection-handler+type=ldap-connection-handler+name=my handler
098 * </pre>
099 *
100 * The final example identifies the global configuration:
101 *
102 * <pre>
103 *  /relation=global-configuration
104 * </pre>
105 *
106 * @param <C>
107 *            The type of client managed object configuration that this path
108 *            references.
109 * @param <S>
110 *            The type of server managed object configuration that this path
111 *            references.
112 */
113public final class ManagedObjectPath<C extends ConfigurationClient, S extends Configuration> {
114
115    /**
116     * A serialize which is used to generate the toDN representation.
117     */
118    private static final class DNSerializer implements ManagedObjectPathSerializer {
119
120        /** The current DN. */
121        private DN dn;
122
123        /** The LDAP profile. */
124        private final LDAPProfile profile;
125
126        /** Create a new DN builder. */
127        private DNSerializer() {
128            this.dn = DN.rootDN();
129            this.profile = LDAPProfile.getInstance();
130        }
131
132        /** {@inheritDoc} */
133        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
134            InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d,
135            String name) {
136            // Add the RDN sequence representing the relation.
137            appendManagedObjectPathElement(r);
138
139            // Now add the single RDN representing the named instance.
140            String type = profile.getRelationChildRDNType(r);
141            AttributeType attrType = Schema.getDefaultSchema().getAttributeType(type);
142            dn = dn.child(new RDN(attrType, name));
143        }
144
145        /** {@inheritDoc} */
146        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
147            SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
148            // Add the RDN sequence representing the relation.
149            appendManagedObjectPathElement(r);
150
151            // Now add the single RDN representing the instance.
152            String type = profile.getRelationChildRDNType(r);
153            AttributeType attrType = Schema.getDefaultSchema().getAttributeType(type);
154            dn = dn.child(new RDN(attrType, d.getName()));
155        }
156
157        /** {@inheritDoc} */
158        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
159            OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
160            // Add the RDN sequence representing the relation.
161            appendManagedObjectPathElement(r);
162        }
163
164        /** {@inheritDoc} */
165        public <C extends ConfigurationClient, S extends Configuration> void appendManagedObjectPathElement(
166            SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
167            // Add the RDN sequence representing the relation.
168            appendManagedObjectPathElement(r);
169        }
170
171        /** Appends the RDN sequence representing the provided relation. */
172        private void appendManagedObjectPathElement(RelationDefinition<?, ?> r) {
173            DN localName = DN.valueOf(profile.getRelationRDNSequence(r));
174            dn = dn.child(localName);
175        }
176
177        /** Gets the serialized DN value. */
178        private DN toDN() {
179            return dn;
180        }
181    }
182
183    /**
184     * Abstract path element.
185     */
186    private static abstract class Element<C extends ConfigurationClient, S extends Configuration> {
187
188        /** The type of managed object referenced by this element. */
189        private final AbstractManagedObjectDefinition<C, S> definition;
190
191        /**
192         * Protected constructor.
193         *
194         * @param definition
195         *            The type of managed object referenced by this element.
196         */
197        protected Element(AbstractManagedObjectDefinition<C, S> definition) {
198            this.definition = definition;
199        }
200
201        /**
202         * Get the managed object definition associated with this element.
203         *
204         * @return Returns the managed object definition associated with this
205         *         element.
206         */
207        public final AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
208            return definition;
209        }
210
211        /**
212         * Get the name associated with this element if applicable.
213         *
214         * @return Returns the name associated with this element if applicable.
215         */
216        public String getName() {
217            return null;
218        }
219
220        /**
221         * Get the relation definition associated with this element.
222         *
223         * @return Returns the relation definition associated with this element.
224         */
225        public abstract RelationDefinition<? super C, ? super S> getRelationDefinition();
226
227        /**
228         * Serialize this path element using the provided serialization
229         * strategy.
230         *
231         * @param serializer
232         *            The managed object path serialization strategy.
233         */
234        public abstract void serialize(ManagedObjectPathSerializer serializer);
235    }
236
237    /**
238     * A path element representing an instantiable managed object.
239     */
240    private static final class InstantiableElement<C extends ConfigurationClient, S extends Configuration> extends
241        Element<C, S> {
242
243        /** Factory method. */
244        private static <C extends ConfigurationClient, S extends Configuration> InstantiableElement<C, S> create(
245            InstantiableRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d,
246            String name) {
247            return new InstantiableElement<>(r, d, name);
248        }
249
250        /** The name of the managed object. */
251        private final String name;
252
253        /** The instantiable relation. */
254        private final InstantiableRelationDefinition<? super C, ? super S> r;
255
256        /** Private constructor. */
257        private InstantiableElement(InstantiableRelationDefinition<? super C, ? super S> r,
258            AbstractManagedObjectDefinition<C, S> d, String name) {
259            super(d);
260            this.r = r;
261            this.name = name;
262        }
263
264        /** {@inheritDoc} */
265        @Override
266        public String getName() {
267            return name;
268        }
269
270        /** {@inheritDoc} */
271        @Override
272        public InstantiableRelationDefinition<? super C, ? super S> getRelationDefinition() {
273            return r;
274        }
275
276        /** {@inheritDoc} */
277        @Override
278        public void serialize(ManagedObjectPathSerializer serializer) {
279            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition(), name);
280        }
281    }
282
283    /**
284     * A path element representing an optional managed object.
285     */
286    private static final class OptionalElement<C extends ConfigurationClient, S extends Configuration> extends
287        Element<C, S> {
288
289        /** Factory method. */
290        private static <C extends ConfigurationClient, S extends Configuration> OptionalElement<C, S> create(
291            OptionalRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
292            return new OptionalElement<>(r, d);
293        }
294
295        /** The optional relation. */
296        private final OptionalRelationDefinition<? super C, ? super S> r;
297
298        /** Private constructor. */
299        private OptionalElement(OptionalRelationDefinition<? super C, ? super S> r,
300            AbstractManagedObjectDefinition<C, S> d) {
301            super(d);
302            this.r = r;
303        }
304
305        /** {@inheritDoc} */
306        @Override
307        public OptionalRelationDefinition<? super C, ? super S> getRelationDefinition() {
308            return r;
309        }
310
311        /** {@inheritDoc} */
312        @Override
313        public void serialize(ManagedObjectPathSerializer serializer) {
314            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
315        }
316    }
317
318    /**
319     * A path element representing an set managed object.
320     */
321    private static final class SetElement<C extends ConfigurationClient, S extends Configuration> extends
322        Element<C, S> {
323
324        /** Factory method. */
325        private static <C extends ConfigurationClient, S extends Configuration> SetElement<C, S> create(
326            SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
327            return new SetElement<>(r, d);
328        }
329
330        /** The set relation. */
331        private final SetRelationDefinition<? super C, ? super S> r;
332
333        /** Private constructor. */
334        private SetElement(SetRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
335            super(d);
336            this.r = r;
337        }
338
339        /** {@inheritDoc} */
340        @Override
341        public SetRelationDefinition<? super C, ? super S> getRelationDefinition() {
342            return r;
343        }
344
345        /** {@inheritDoc} */
346        @Override
347        public void serialize(ManagedObjectPathSerializer serializer) {
348            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
349        }
350    }
351
352    /**
353     * A path element representing a singleton managed object.
354     */
355    private static final class SingletonElement<C extends ConfigurationClient, S extends Configuration> extends
356        Element<C, S> {
357
358        /** Factory method. */
359        private static <C extends ConfigurationClient, S extends Configuration> SingletonElement<C, S> create(
360            SingletonRelationDefinition<? super C, ? super S> r, AbstractManagedObjectDefinition<C, S> d) {
361            return new SingletonElement<>(r, d);
362        }
363
364        /** The singleton relation. */
365        private final SingletonRelationDefinition<? super C, ? super S> r;
366
367        /** Private constructor. */
368        private SingletonElement(SingletonRelationDefinition<? super C, ? super S> r,
369            AbstractManagedObjectDefinition<C, S> d) {
370            super(d);
371            this.r = r;
372        }
373
374        /** {@inheritDoc} */
375        @Override
376        public SingletonRelationDefinition<? super C, ? super S> getRelationDefinition() {
377            return r;
378        }
379
380        /** {@inheritDoc} */
381        @Override
382        public void serialize(ManagedObjectPathSerializer serializer) {
383            serializer.appendManagedObjectPathElement(r, getManagedObjectDefinition());
384        }
385    }
386
387    /**
388     * A serialize which is used to generate the toString representation.
389     */
390    private static final class StringSerializer implements ManagedObjectPathSerializer {
391
392        /** Serialize to this string builder. */
393        private final StringBuilder builder;
394
395        /** Private constructor. */
396        private StringSerializer(StringBuilder builder) {
397            this.builder = builder;
398        }
399
400        /** {@inheritDoc} */
401        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
402            InstantiableRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d,
403            String name) {
404            serializeElement(r, d);
405
406            // Be careful to escape any forward slashes in the name.
407            builder.append("+name=");
408            builder.append(name.replace("/", "//"));
409        }
410
411        /** {@inheritDoc} */
412        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
413            OptionalRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
414            serializeElement(r, d);
415        }
416
417        /** {@inheritDoc} */
418        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
419            SingletonRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
420            serializeElement(r, d);
421        }
422
423        /** {@inheritDoc} */
424        public <M extends ConfigurationClient, N extends Configuration> void appendManagedObjectPathElement(
425            SetRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
426            serializeElement(r, d);
427        }
428
429        /** Common element serialization. */
430        private <M, N> void serializeElement(RelationDefinition<?, ?> r, AbstractManagedObjectDefinition<?, ?> d) {
431            // Always specify the relation name.
432            builder.append("/relation=");
433            builder.append(r.getName());
434
435            // Only specify the type if it is a sub-type of the relation's
436            // type.
437            if (r.getChildDefinition() != d) {
438                builder.append("+type=");
439                builder.append(d.getName());
440            }
441        }
442    }
443
444    /** Single instance of a root path. */
445    private static final ManagedObjectPath<RootCfgClient, RootCfg> EMPTY_PATH =
446        new ManagedObjectPath<>(new LinkedList<Element<?, ?>>(), null, RootCfgDefn.getInstance());
447
448    /** A regular expression used to parse path elements. */
449    private static final Pattern PE_REGEXP = Pattern.compile("^\\s*relation=\\s*([^+]+)\\s*"
450        + "(\\+\\s*type=\\s*([^+]+)\\s*)?" + "(\\+\\s*name=\\s*([^+]+)\\s*)?$");
451
452    /**
453     * Creates a new managed object path representing the configuration root.
454     *
455     * @return Returns a new managed object path representing the configuration
456     *         root.
457     */
458    public static ManagedObjectPath<RootCfgClient, RootCfg> emptyPath() {
459        return EMPTY_PATH;
460    }
461
462    /**
463     * Returns a managed object path holding the value of the specified string.
464     *
465     * @param s
466     *            The string to be parsed.
467     * @return Returns a managed object path holding the value of the specified
468     *         string.
469     * @throws IllegalArgumentException
470     *             If the string could not be parsed.
471     */
472    public static ManagedObjectPath<?, ?> valueOf(String s) {
473        String ns = s.trim();
474
475        // Check for root special case.
476        if (ns.equals("/")) {
477            return EMPTY_PATH;
478        }
479
480        // Parse the elements.
481        LinkedList<Element<?, ?>> elements = new LinkedList<>();
482        Element<?, ?> lastElement = null;
483        AbstractManagedObjectDefinition<?, ?> definition = RootCfgDefn.getInstance();
484
485        if (!ns.startsWith("/")) {
486            throw new IllegalArgumentException("Invalid path \"" + ns + "\": must begin with a \"/\"");
487        }
488
489        int start = 1;
490        while (true) {
491            // Get the next path element.
492            int end;
493            for (end = start; end < ns.length(); end++) {
494                char c = ns.charAt(end);
495                if (c == '/') {
496                    if (end == (ns.length() - 1)) {
497                        throw new IllegalArgumentException("Invalid path \"" + ns
498                            + "\": must not end with a trailing \"/\"");
499                    }
500
501                    if (ns.charAt(end + 1) == '/') {
502                        // Found an escaped forward slash.
503                        end++;
504                    } else {
505                        // Found the end of this path element.
506                        break;
507                    }
508                }
509            }
510
511            // Get the next element.
512            String es = ns.substring(start, end);
513
514            Matcher m = PE_REGEXP.matcher(es);
515            if (!m.matches()) {
516                throw new IllegalArgumentException("Invalid path element \"" + es + "\" in path \"" + ns + "\"");
517            }
518
519            // Mandatory.
520            String relation = m.group(1);
521
522            // Optional.
523            String type = m.group(3);
524
525            // Mandatory if relation is instantiable.
526            String name = m.group(5);
527
528            // Get the relation definition.
529            RelationDefinition<?, ?> r;
530            try {
531                r = definition.getRelationDefinition(relation);
532            } catch (IllegalArgumentException e) {
533                throw new IllegalArgumentException("Invalid path element \"" + es + "\" in path \"" + ns
534                    + "\": unknown relation \"" + relation + "\"");
535            }
536
537            // Append the next element.
538            lastElement = createElement(r, ns, es, type, name);
539            elements.add(lastElement);
540            definition = lastElement.getManagedObjectDefinition();
541
542            // Update start to point to the beginning of the next element.
543            if (end < ns.length()) {
544                // Skip to the beginning of the next element
545                start = end + 1;
546            } else {
547                // We reached the end of the string.
548                break;
549            }
550        }
551
552        // Construct the new path.
553        return create(elements, lastElement);
554    }
555
556    /**
557     * Factory method required in order to allow generic wild-card
558     * construction of new paths.
559     */
560    private static <C extends ConfigurationClient, S extends Configuration> ManagedObjectPath<C, S> create(
561        LinkedList<Element<?, ?>> elements, Element<C, S> lastElement) {
562        return new ManagedObjectPath<>(elements, lastElement.getRelationDefinition(),
563            lastElement.getManagedObjectDefinition());
564    }
565
566    // @Checkstyle:ignore
567    /** Decode an element. */
568    private static <C extends ConfigurationClient, S extends Configuration> Element<? extends C, ? extends S>
569    createElement(RelationDefinition<C, S> r, String path, String element, String type, String name) {
570        // First determine the managed object definition.
571        AbstractManagedObjectDefinition<? extends C, ? extends S> d = null;
572
573        if (type != null) {
574            for (AbstractManagedObjectDefinition<? extends C, ? extends S> child : r.getChildDefinition()
575                .getAllChildren()) {
576                if (child.getName().equals(type)) {
577                    d = child;
578                    break;
579                }
580            }
581
582            if (d == null) {
583                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
584                    + "\": unknown sub-type \"" + type + "\"");
585            }
586        } else {
587            d = r.getChildDefinition();
588        }
589
590        if (r instanceof InstantiableRelationDefinition) {
591            InstantiableRelationDefinition<C, S> ir = (InstantiableRelationDefinition<C, S>) r;
592
593            if (name == null) {
594                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
595                    + "\": no instance name for instantiable relation");
596            }
597
598            return InstantiableElement.create(ir, d, name);
599        } else if (r instanceof SetRelationDefinition) {
600            SetRelationDefinition<C, S> ir = (SetRelationDefinition<C, S>) r;
601
602            if (name != null) {
603                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
604                    + "\": instance name specified for set relation");
605            }
606
607            return SetElement.create(ir, d);
608        } else if (r instanceof OptionalRelationDefinition) {
609            OptionalRelationDefinition<C, S> or = (OptionalRelationDefinition<C, S>) r;
610
611            if (name != null) {
612                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
613                    + "\": instance name specified for optional relation");
614            }
615
616            return OptionalElement.create(or, d);
617        } else if (r instanceof SingletonRelationDefinition) {
618            SingletonRelationDefinition<C, S> sr = (SingletonRelationDefinition<C, S>) r;
619
620            if (name != null) {
621                throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
622                    + "\": instance name specified for singleton relation");
623            }
624
625            return SingletonElement.create(sr, d);
626        } else {
627            throw new IllegalArgumentException("Invalid path element \"" + element + "\" in path \"" + path
628                + "\": unsupported relation type");
629        }
630    }
631
632    /** The managed object definition in this path. */
633    private final AbstractManagedObjectDefinition<C, S> d;
634
635    /** The list of path elements in this path. */
636    private final List<Element<?, ?>> elements;
637
638    /** The last relation definition in this path. */
639    private final RelationDefinition<? super C, ? super S> r;
640
641    /** Private constructor. */
642    private ManagedObjectPath(LinkedList<Element<?, ?>> elements, RelationDefinition<? super C, ? super S> r,
643        AbstractManagedObjectDefinition<C, S> d) {
644        this.elements = Collections.unmodifiableList(elements);
645        this.r = r;
646        this.d = d;
647    }
648
649    /**
650     * Creates a new managed object path which has the same structure as this
651     * path except that the final path element is associated with the specified
652     * managed object definition.
653     *
654     * @param <C1>
655     *            The type of client managed object configuration that this path
656     *            will reference.
657     * @param <S1>
658     *            The type of server managed object configuration that this path
659     *            will reference.
660     * @param nd
661     *            The new managed object definition.
662     * @return Returns a new managed object path which has the same structure as
663     *         this path except that the final path element is associated with
664     *         the specified managed object definition.
665     */
666    // @Checkstyle:ignore
667    public <C1 extends C, S1 extends S> ManagedObjectPath<C1, S1> asSubType(AbstractManagedObjectDefinition<C1, S1> nd) {
668        if (r instanceof InstantiableRelationDefinition) {
669            InstantiableRelationDefinition<? super C, ? super S> ir =
670                (InstantiableRelationDefinition<? super C, ? super S>) r;
671            if (elements.size() == 0) {
672                return parent().child(ir, nd, "null");
673            } else {
674                return parent().child(ir, nd, elements.get(elements.size() - 1).getName());
675            }
676        } else if (r instanceof SetRelationDefinition) {
677            SetRelationDefinition<? super C, ? super S> sr = (SetRelationDefinition<? super C, ? super S>) r;
678            return parent().child(sr, nd);
679        } else if (r instanceof OptionalRelationDefinition) {
680            OptionalRelationDefinition<? super C, ? super S> or =
681                (OptionalRelationDefinition<? super C, ? super S>) r;
682            return parent().child(or, nd);
683        } else {
684            SingletonRelationDefinition<? super C, ? super S> sr =
685                (SingletonRelationDefinition<? super C, ? super S>) r;
686            return parent().child(sr, nd);
687        }
688    }
689
690    /**
691     * Creates a new child managed object path beneath the provided parent path
692     * having the specified managed object definition.
693     *
694     * @param <M>
695     *            The type of client managed object configuration that the child
696     *            path references.
697     * @param <N>
698     *            The type of server managed object configuration that the child
699     *            path references.
700     * @param r
701     *            The instantiable relation referencing the child.
702     * @param d
703     *            The managed object definition associated with the child (must
704     *            be a sub-type of the relation).
705     * @param name
706     *            The relative name of the child managed object.
707     * @return Returns a new child managed object path beneath the provided
708     *         parent path.
709     * @throws IllegalArgumentException
710     *             If the provided name is empty or blank.
711     */
712    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
713        InstantiableRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d, String name) {
714        if (name.trim().length() == 0) {
715            throw new IllegalArgumentException("Empty or blank managed object names are not allowed");
716        }
717        LinkedList<Element<?, ?>> celements = new LinkedList<>(elements);
718        celements.add(new InstantiableElement<M, N>(r, d, name));
719        return new ManagedObjectPath<>(celements, r, d);
720    }
721
722    /**
723     * Creates a new child managed object path beneath the provided parent path
724     * using the relation's child managed object definition.
725     *
726     * @param <M>
727     *            The type of client managed object configuration that the child
728     *            path references.
729     * @param <N>
730     *            The type of server managed object configuration that the child
731     *            path references.
732     * @param r
733     *            The instantiable relation referencing the child.
734     * @param name
735     *            The relative name of the child managed object.
736     * @return Returns a new child managed object path beneath the provided
737     *         parent path.
738     * @throws IllegalArgumentException
739     *             If the provided name is empty or blank.
740     */
741    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
742        InstantiableRelationDefinition<M, N> r, String name) {
743        return child(r, r.getChildDefinition(), name);
744    }
745
746    /**
747     * Creates a new child managed object path beneath the provided parent path
748     * having the specified managed object definition.
749     *
750     * @param <M>
751     *            The type of client managed object configuration that the child
752     *            path references.
753     * @param <N>
754     *            The type of server managed object configuration that the child
755     *            path references.
756     * @param r
757     *            The optional relation referencing the child.
758     * @param d
759     *            The managed object definition associated with the child (must
760     *            be a sub-type of the relation).
761     * @return Returns a new child managed object path beneath the provided
762     *         parent path.
763     */
764    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
765        OptionalRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
766        LinkedList<Element<?, ?>> celements = new LinkedList<>(elements);
767        celements.add(new OptionalElement<M, N>(r, d));
768        return new ManagedObjectPath<>(celements, r, d);
769    }
770
771    /**
772     * Creates a new child managed object path beneath the provided parent path
773     * using the relation's child managed object definition.
774     *
775     * @param <M>
776     *            The type of client managed object configuration that the child
777     *            path references.
778     * @param <N>
779     *            The type of server managed object configuration that the child
780     *            path references.
781     * @param r
782     *            The optional relation referencing the child.
783     * @return Returns a new child managed object path beneath the provided
784     *         parent path.
785     */
786    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
787        OptionalRelationDefinition<M, N> r) {
788        return child(r, r.getChildDefinition());
789    }
790
791    /**
792     * Creates a new child managed object path beneath the provided parent path
793     * having the specified managed object definition.
794     *
795     * @param <M>
796     *            The type of client managed object configuration that the child
797     *            path references.
798     * @param <N>
799     *            The type of server managed object configuration that the child
800     *            path references.
801     * @param r
802     *            The singleton relation referencing the child.
803     * @param d
804     *            The managed object definition associated with the child (must
805     *            be a sub-type of the relation).
806     * @return Returns a new child managed object path beneath the provided
807     *         parent path.
808     */
809    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
810        SingletonRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
811        LinkedList<Element<?, ?>> celements = new LinkedList<>(elements);
812        celements.add(new SingletonElement<M, N>(r, d));
813        return new ManagedObjectPath<>(celements, r, d);
814    }
815
816    /**
817     * Creates a new child managed object path beneath the provided parent path
818     * using the relation's child managed object definition.
819     *
820     * @param <M>
821     *            The type of client managed object configuration that the child
822     *            path references.
823     * @param <N>
824     *            The type of server managed object configuration that the child
825     *            path references.
826     * @param r
827     *            The singleton relation referencing the child.
828     * @return Returns a new child managed object path beneath the provided
829     *         parent path.
830     */
831    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
832        SingletonRelationDefinition<M, N> r) {
833        return child(r, r.getChildDefinition());
834    }
835
836    /**
837     * Creates a new child managed object path beneath the provided parent path
838     * having the specified managed object definition.
839     *
840     * @param <M>
841     *            The type of client managed object configuration that the child
842     *            path references.
843     * @param <N>
844     *            The type of server managed object configuration that the child
845     *            path references.
846     * @param r
847     *            The set relation referencing the child.
848     * @param d
849     *            The managed object definition associated with the child (must
850     *            be a sub-type of the relation).
851     * @return Returns a new child managed object path beneath the provided
852     *         parent path.
853     * @throws IllegalArgumentException
854     *             If the provided name is empty or blank.
855     */
856    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
857        SetRelationDefinition<? super M, ? super N> r, AbstractManagedObjectDefinition<M, N> d) {
858        LinkedList<Element<?, ?>> celements = new LinkedList<>(elements);
859        celements.add(new SetElement<M, N>(r, d));
860        return new ManagedObjectPath<>(celements, r, d);
861    }
862
863    /**
864     * Creates a new child managed object path beneath the provided parent path
865     * having the managed object definition indicated by <code>name</code>.
866     *
867     * @param <M>
868     *            The type of client managed object configuration that the path
869     *            references.
870     * @param <N>
871     *            The type of server managed object configuration that the path
872     *            references.
873     * @param r
874     *            The set relation referencing the child.
875     * @param name
876     *            The name of the managed object definition associated with the
877     *            child (must be a sub-type of the relation).
878     * @return Returns a new child managed object path beneath the provided
879     *         parent path.
880     * @throws IllegalArgumentException
881     *             If the provided name is empty or blank or specifies a managed
882     *             object definition which is not a sub-type of the relation's
883     *             child definition.
884     */
885    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<? extends M, ? extends N> child(
886        SetRelationDefinition<M, N> r, String name) {
887        AbstractManagedObjectDefinition<M, N> d = r.getChildDefinition();
888        return child(r, d.getChild(name));
889    }
890
891    /**
892     * Creates a new child managed object path beneath the provided parent path
893     * using the relation's child managed object definition.
894     *
895     * @param <M>
896     *            The type of client managed object configuration that the child
897     *            path references.
898     * @param <N>
899     *            The type of server managed object configuration that the child
900     *            path references.
901     * @param r
902     *            The set relation referencing the child.
903     * @return Returns a new child managed object path beneath the provided
904     *         parent path.
905     * @throws IllegalArgumentException
906     *             If the provided name is empty or blank.
907     */
908    public <M extends ConfigurationClient, N extends Configuration> ManagedObjectPath<M, N> child(
909        SetRelationDefinition<M, N> r) {
910        return child(r, r.getChildDefinition());
911    }
912
913    /** {@inheritDoc} */
914    @Override
915    public boolean equals(Object obj) {
916        if (obj == this) {
917            return true;
918        } else if (obj instanceof ManagedObjectPath) {
919            ManagedObjectPath<?, ?> other = (ManagedObjectPath<?, ?>) obj;
920            return toString().equals(other.toString());
921        } else {
922            return false;
923        }
924    }
925
926    /**
927     * Get the definition of the managed object referred to by this path.
928     * <p>
929     * When the path is empty, the {@link RootCfgDefn} is returned.
930     *
931     * @return Returns the definition of the managed object referred to by this
932     *         path, or the {@link RootCfgDefn} if the path is empty.
933     */
934    public AbstractManagedObjectDefinition<C, S> getManagedObjectDefinition() {
935        return d;
936    }
937
938    /**
939     * Get the name of the managed object referred to by this path if
940     * applicable.
941     * <p>
942     * If there path does not refer to an instantiable managed object
943     * <code>null</code> is returned.
944     *
945     * @return Returns the name of the managed object referred to by this path,
946     *         or <code>null</code> if the managed object does not have a name.
947     */
948    public String getName() {
949        if (elements.isEmpty()) {
950            return null;
951        } else {
952            return elements.get(elements.size() - 1).getName();
953        }
954    }
955
956    /**
957     * Get the relation definition of the managed object referred to by this
958     * path.
959     * <p>
960     * When the path is empty, the <code>null</code> is returned.
961     *
962     * @return Returns the relation definition of the managed object referred to
963     *         by this path, or the <code>null</code> if the path is empty.
964     */
965    public RelationDefinition<? super C, ? super S> getRelationDefinition() {
966        return r;
967    }
968
969    /** {@inheritDoc} */
970    @Override
971    public int hashCode() {
972        return toString().hashCode();
973    }
974
975    /**
976     * Determine whether or not this path contains any path elements.
977     *
978     * @return Returns <code>true</code> if this path does not contain any path
979     *         elements.
980     */
981    public boolean isEmpty() {
982        return elements.isEmpty();
983    }
984
985    /**
986     * Determines whether this managed object path references the same location
987     * as the provided managed object path.
988     * <p>
989     * This method differs from <code>equals</code> in that it ignores sub-type
990     * definitions.
991     *
992     * @param other
993     *            The managed object path to be compared.
994     * @return Returns <code>true</code> if this managed object path references
995     *         the same location as the provided managed object path.
996     */
997    public boolean matches(ManagedObjectPath<?, ?> other) {
998        DN thisDN = toDN();
999        DN otherDN = other.toDN();
1000        return thisDN.equals(otherDN);
1001    }
1002
1003    /**
1004     * Creates a new parent managed object path representing the immediate
1005     * parent of this path. This method is a short-hand for
1006     * <code>parent(1)</code>.
1007     *
1008     * @return Returns a new parent managed object path representing the
1009     *         immediate parent of this path.
1010     * @throws IllegalArgumentException
1011     *             If this path does not have a parent (i.e. it is the empty
1012     *             path).
1013     */
1014    public ManagedObjectPath<?, ?> parent() {
1015        return parent(1);
1016    }
1017
1018    /**
1019     * Creates a new parent managed object path the specified number of path
1020     * elements above this path.
1021     *
1022     * @param offset
1023     *            The number of path elements (0 - means no offset, 1 means the
1024     *            parent, and 2 means the grand-parent).
1025     * @return Returns a new parent managed object path the specified number of
1026     *         path elements above this path.
1027     * @throws IllegalArgumentException
1028     *             If the offset is less than 0, or greater than the number of
1029     *             path elements in this path.
1030     */
1031    public ManagedObjectPath<?, ?> parent(int offset) {
1032        if (offset < 0) {
1033            throw new IllegalArgumentException("Negative offset");
1034        }
1035
1036        if (offset > elements.size()) {
1037            throw new IllegalArgumentException("Offset is greater than the number of path elements");
1038        }
1039
1040        // An offset of 0 leaves the path unchanged.
1041        if (offset == 0) {
1042            return this;
1043        }
1044
1045        // Return the empty path if the parent has zero elements.
1046        if (elements.size() == offset) {
1047            return emptyPath();
1048        }
1049
1050        LinkedList<Element<?, ?>> celements = new LinkedList<>(elements.subList(0, elements.size() - offset));
1051        return create(celements, celements.getLast());
1052    }
1053
1054    /**
1055     * Creates a new managed object path which has the same structure as this
1056     * path except that the final path element is renamed. The final path
1057     * element must comprise of an instantiable relation.
1058     *
1059     * @param newName
1060     *            The new name of the final path element.
1061     * @return Returns a new managed object path which has the same structure as
1062     *         this path except that the final path element is renamed.
1063     * @throws IllegalStateException
1064     *             If this managed object path is empty or if its final path
1065     *             element does not comprise of an instantiable relation.
1066     */
1067    public ManagedObjectPath<C, S> rename(String newName) {
1068        if (elements.size() == 0) {
1069            throw new IllegalStateException("Cannot rename an empty path");
1070        }
1071
1072        if (r instanceof InstantiableRelationDefinition) {
1073            InstantiableRelationDefinition<? super C, ? super S> ir =
1074                (InstantiableRelationDefinition<? super C, ? super S>) r;
1075            return parent().child(ir, d, newName);
1076        } else {
1077            throw new IllegalStateException("Not an instantiable relation");
1078        }
1079    }
1080
1081    /**
1082     * Serialize this managed object path using the provided serialization
1083     * strategy.
1084     * <p>
1085     * The path elements will be passed to the serializer in big-endian order:
1086     * starting from the root element and proceeding down to the leaf.
1087     *
1088     * @param serializer
1089     *            The managed object path serialization strategy.
1090     */
1091    public void serialize(ManagedObjectPathSerializer serializer) {
1092        for (Element<?, ?> element : elements) {
1093            element.serialize(serializer);
1094        }
1095    }
1096
1097    /**
1098     * Get the number of path elements in this managed object path.
1099     *
1100     * @return Returns the number of path elements (0 - means no offset, 1 means
1101     *         the parent, and 2 means the grand-parent).
1102     */
1103    public int size() {
1104        return elements.size();
1105    }
1106
1107    /**
1108     * Creates a DN representation of this managed object path.
1109     *
1110     * @return Returns a DN representation of this managed object path.
1111     */
1112    public DN toDN() {
1113        // Use a simple serializer to create the contents.
1114        DNSerializer serializer = new DNSerializer();
1115        serialize(serializer);
1116        return serializer.toDN();
1117    }
1118
1119    /** {@inheritDoc} */
1120    @Override
1121    public String toString() {
1122        StringBuilder builder = new StringBuilder();
1123        toString(builder);
1124        return builder.toString();
1125    }
1126
1127    /**
1128     * Appends a string representation of this managed object path to the
1129     * provided string builder.
1130     *
1131     * @param builder
1132     *            Append the string representation to this builder.
1133     * @see #toString()
1134     */
1135    public void toString(final StringBuilder builder) {
1136        if (isEmpty()) {
1137            // Special treatment of root configuration paths.
1138            builder.append('/');
1139        } else {
1140            // Use a simple serializer to create the contents.
1141            ManagedObjectPathSerializer serializer = new StringSerializer(builder);
1142            serialize(serializer);
1143        }
1144    }
1145
1146}