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