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