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