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