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