416ce36b3d132f2f9664ace6cb0293579ecc9b53
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / InstanceIdentifier.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.binding;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.base.MoreObjects;
15 import com.google.common.base.MoreObjects.ToStringHelper;
16 import com.google.common.base.VerifyException;
17 import com.google.common.collect.ImmutableCollection;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.Iterables;
20 import java.io.ObjectStreamException;
21 import java.io.Serializable;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.Objects;
25 import java.util.Optional;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.eclipse.jdt.annotation.Nullable;
28 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
29 import org.opendaylight.yangtools.util.HashCodeBuilder;
30
31 /**
32  * This instance identifier uniquely identifies a specific DataObject in the data tree modeled by YANG.
33  *
34  * <p>
35  * For Example let's say you were trying to refer to a node in inventory which was modeled in YANG as follows,
36  *
37  * <p>
38  * <pre>
39  * module opendaylight-inventory {
40  *      ....
41  *
42  *      container nodes {
43  *        list node {
44  *            key "id";
45  *            ext:context-instance "node-context";
46  *
47  *            uses node;
48  *        }
49  *    }
50  *
51  * }
52  * </pre>
53  *
54  * <p>
55  * You can create an instance identifier as follows to get to a node with id "openflow:1": {@code
56  * InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build();
57  * }
58  *
59  * <p>
60  * This would be the same as using a path like so, "/nodes/node/openflow:1" to refer to the openflow:1 node
61  */
62 public class InstanceIdentifier<T extends DataObject>
63         implements HierarchicalIdentifier<InstanceIdentifier<? extends DataObject>> {
64     private static final long serialVersionUID = 3L;
65
66     /*
67      * Protected to differentiate internal and external access. Internal access is required never to modify
68      * the contents. References passed to outside entities have to be wrapped in an unmodifiable view.
69      */
70     final Iterable<PathArgument> pathArguments;
71
72     private final @NonNull Class<T> targetType;
73     private final boolean wildcarded;
74     private final int hash;
75
76     InstanceIdentifier(final Class<T> type, final Iterable<PathArgument> pathArguments, final boolean wildcarded,
77             final int hash) {
78         this.pathArguments = requireNonNull(pathArguments);
79         this.targetType = requireNonNull(type);
80         this.wildcarded = wildcarded;
81         this.hash = hash;
82     }
83
84     /**
85      * Return the type of data which this InstanceIdentifier identifies.
86      *
87      * @return Target type
88      */
89     public final @NonNull Class<T> getTargetType() {
90         return targetType;
91     }
92
93     /**
94      * Perform a safe target type adaptation of this instance identifier to target type. This method is useful when
95      * dealing with type-squashed instances.
96      *
97      * @return Path argument with target type
98      * @throws VerifyException if this instance identifier cannot be adapted to target type
99      * @throws NullPointerException if {@code target} is null
100      */
101     @SuppressWarnings("unchecked")
102     public final <N extends DataObject> @NonNull InstanceIdentifier<N> verifyTarget(final Class<@NonNull N> target) {
103         verify(target.equals(targetType), "Cannot adapt %s to %s", this, target);
104         return (InstanceIdentifier<N>) this;
105     }
106
107     /**
108      * Return the path argument chain which makes up this instance identifier.
109      *
110      * @return Path argument chain. Immutable and does not contain nulls.
111      */
112     public final @NonNull Iterable<PathArgument> getPathArguments() {
113         return Iterables.unmodifiableIterable(pathArguments);
114     }
115
116     /**
117      * Check whether an instance identifier contains any wildcards. A wildcard is an path argument which has a null key.
118      *
119      * @return true if any of the path arguments has a null key.
120      */
121     public final boolean isWildcarded() {
122         return wildcarded;
123     }
124
125     @Override
126     public final int hashCode() {
127         return hash;
128     }
129
130     @Override
131     public final boolean equals(final Object obj) {
132         if (this == obj) {
133             return true;
134         }
135         if (obj == null) {
136             return false;
137         }
138         if (getClass() != obj.getClass()) {
139             return false;
140         }
141
142         final InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
143         if (pathArguments == other.pathArguments) {
144             return true;
145         }
146
147         /*
148          * We could now just go and compare the pathArguments, but that
149          * can be potentially expensive. Let's try to avoid that by
150          * checking various things that we have cached from pathArguments
151          * and trying to prove the identifiers are *not* equal.
152          */
153         if (hash != other.hash) {
154             return false;
155         }
156         if (wildcarded != other.wildcarded) {
157             return false;
158         }
159         if (targetType != other.targetType) {
160             return false;
161         }
162         if (fastNonEqual(other)) {
163             return false;
164         }
165
166         // Everything checks out so far, so we have to do a full equals
167         return Iterables.elementsEqual(pathArguments, other.pathArguments);
168     }
169
170     /**
171      * Perform class-specific fast checks for non-equality. This allows subclasses to avoid iterating over the
172      * pathArguments by performing quick checks on their specific fields.
173      *
174      * @param other The other identifier, guaranteed to be the same class
175      * @return true if the other identifier cannot be equal to this one.
176      */
177     protected boolean fastNonEqual(final InstanceIdentifier<?> other) {
178         return false;
179     }
180
181     @Override
182     public final String toString() {
183         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
184     }
185
186     /**
187      * Add class-specific toString attributes.
188      *
189      * @param toStringHelper ToStringHelper instance
190      * @return ToStringHelper instance which was passed in
191      */
192     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
193         return toStringHelper.add("targetType", targetType).add("path", Iterables.toString(pathArguments));
194     }
195
196     /**
197      * Return an instance identifier trimmed at the first occurrence of a specific component type.
198      *
199      * <p>
200      * For example let's say an instance identifier was built like so,
201      * <pre>
202      *      identifier = InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class,
203      *                   new NodeKey(new NodeId("openflow:1")).build();
204      * </pre>
205      *
206      * <p>
207      * And you wanted to obtain the Instance identifier which represented Nodes you would do it like so,
208      *
209      * <p>
210      * <pre>
211      *      identifier.firstIdentifierOf(Nodes.class)
212      * </pre>
213      *
214      * @param type component type
215      * @return trimmed instance identifier, or null if the component type
216      *         is not present.
217      */
218     public final <I extends DataObject> @Nullable InstanceIdentifier<I> firstIdentifierOf(
219             final Class<@NonNull I> type) {
220         int count = 1;
221         for (final PathArgument a : pathArguments) {
222             if (type.equals(a.getType())) {
223                 @SuppressWarnings("unchecked")
224                 final InstanceIdentifier<I> ret = (InstanceIdentifier<I>) internalCreate(
225                         Iterables.limit(pathArguments, count));
226                 return ret;
227             }
228
229             ++count;
230         }
231
232         return null;
233     }
234
235     /**
236      * Return the key associated with the first component of specified type in
237      * an identifier.
238      *
239      * @param listItem component type
240      * @return key associated with the component, or null if the component type
241      *         is not present.
242      */
243     public final <N extends Identifiable<K> & DataObject, K extends Identifier<N>> @Nullable K firstKeyOf(
244             final Class<@NonNull N> listItem) {
245         for (final PathArgument i : pathArguments) {
246             if (listItem.equals(i.getType())) {
247                 @SuppressWarnings("unchecked")
248                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
249                 return ret;
250             }
251         }
252
253         return null;
254     }
255
256     /**
257      * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all
258      * PathArguments to match exactly.
259      *
260      * <p>
261      * The contains method checks if the other identifier is fully contained within the current identifier. It does this
262      * by looking at only the types of the path arguments and not by comparing the path arguments themselves.
263      *
264      * <p>
265      * To illustrate here is an example which explains the working of this API. Let's say you have two instance
266      * identifiers as follows:
267      * {@code
268      * this = /nodes/node/openflow:1
269      * other = /nodes/node/openflow:2
270      * }
271      * then this.contains(other) will return false.
272      *
273      * @param other Potentially-container instance identifier
274      * @return True if the specified identifier is contained in this identifier.
275      */
276     @Override
277     public final boolean contains(final InstanceIdentifier<? extends DataObject> other) {
278         requireNonNull(other, "other should not be null");
279
280         final Iterator<?> lit = pathArguments.iterator();
281         final Iterator<?> oit = other.pathArguments.iterator();
282
283         while (lit.hasNext()) {
284             if (!oit.hasNext()) {
285                 return false;
286             }
287
288             if (!lit.next().equals(oit.next())) {
289                 return false;
290             }
291         }
292
293         return true;
294     }
295
296     /**
297      * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar
298      * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their
299      * non-wildcarded PathArgument counterpart.
300      *
301      * @param other Identifier which should be checked for inclusion.
302      * @return true if this identifier contains the other object
303      */
304     public final boolean containsWildcarded(final InstanceIdentifier<?> other) {
305         requireNonNull(other, "other should not be null");
306
307         final Iterator<PathArgument> lit = pathArguments.iterator();
308         final Iterator<PathArgument> oit = other.pathArguments.iterator();
309
310         while (lit.hasNext()) {
311             if (!oit.hasNext()) {
312                 return false;
313             }
314
315             final PathArgument la = lit.next();
316             final PathArgument oa = oit.next();
317
318             if (!la.getType().equals(oa.getType())) {
319                 return false;
320             }
321             if (la instanceof IdentifiableItem<?, ?> && oa instanceof IdentifiableItem<?, ?> && !la.equals(oa)) {
322                 return false;
323             }
324         }
325
326         return true;
327     }
328
329     private <N extends DataObject> @NonNull InstanceIdentifier<N> childIdentifier(final AbstractPathArgument<N> arg) {
330         return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)),
331             HashCodeBuilder.nextHashCode(hash, arg), isWildcarded());
332     }
333
334     /**
335      * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
336      * {@code builder().child(container).build()}.
337      *
338      * @param container Container to append
339      * @param <N> Container type
340      * @return An InstanceIdentifier.
341      * @throws NullPointerException if {@code container} is null
342      */
343     public final <N extends ChildOf<? super T>> @NonNull InstanceIdentifier<N> child(
344             final Class<@NonNull N> container) {
345         return childIdentifier(Item.of(container));
346     }
347
348     /**
349      * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
350      * {@code builder().child(listItem, listKey).build()}.
351      *
352      * @param listItem List to append
353      * @param listKey List key
354      * @param <N> List type
355      * @param <K> Key type
356      * @return An InstanceIdentifier.
357      * @throws NullPointerException if any argument is null
358      */
359     @SuppressWarnings("unchecked")
360     public final <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>>
361             @NonNull KeyedInstanceIdentifier<N, K> child(final Class<@NonNull N> listItem, final K listKey) {
362         return (KeyedInstanceIdentifier<N, K>) childIdentifier(IdentifiableItem.of(listItem, listKey));
363     }
364
365     /**
366      * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
367      * {@code builder().child(caze, container).build()}.
368      *
369      * @param caze Choice case class
370      * @param container Container to append
371      * @param <C> Case type
372      * @param <N> Container type
373      * @return An InstanceIdentifier.
374      * @throws NullPointerException if any argument is null
375      */
376     // FIXME: add a proper caller
377     public final <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
378             @NonNull InstanceIdentifier<N> child(final Class<@NonNull C> caze, final Class<@NonNull N> container) {
379         return childIdentifier(Item.of(caze, container));
380     }
381
382     /**
383      * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
384      * {@code builder().child(caze, listItem, listKey).build()}.
385      *
386      * @param caze Choice case class
387      * @param listItem List to append
388      * @param listKey List key
389      * @param <C> Case type
390      * @param <N> List type
391      * @param <K> Key type
392      * @return An InstanceIdentifier.
393      * @throws NullPointerException if any argument is null
394      */
395     // FIXME: add a proper caller
396     @SuppressWarnings("unchecked")
397     public final <C extends ChoiceIn<? super T> & DataObject, K extends Identifier<N>,
398         N extends Identifiable<K> & ChildOf<? super C>> @NonNull KeyedInstanceIdentifier<N, K> child(
399                 final Class<@NonNull C> caze, final Class<@NonNull N> listItem, final K listKey) {
400         return (KeyedInstanceIdentifier<N, K>) childIdentifier(IdentifiableItem.of(caze, listItem, listKey));
401     }
402
403     /**
404      * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to
405      * {@code builder().augmentation(container).build()}.
406      *
407      * @param container Container to append
408      * @param <N> Container type
409      * @return An InstanceIdentifier.
410      * @throws NullPointerException if {@code container} is null
411      */
412     public final <N extends DataObject & Augmentation<? super T>> @NonNull InstanceIdentifier<N> augmentation(
413             final Class<@NonNull N> container) {
414         return childIdentifier(Item.of(container));
415     }
416
417     /**
418      * Create a builder rooted at this key.
419      *
420      * @return A builder instance
421      */
422     public @NonNull InstanceIdentifierBuilder<T> builder() {
423         return new InstanceIdentifierBuilderImpl<>(Item.of(targetType), pathArguments, hash, isWildcarded());
424     }
425
426     /**
427      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container.
428      *
429      * @param container Base container
430      * @param <T> Type of the container
431      * @return A new {@link InstanceIdentifierBuilder}
432      * @throws NullPointerException if {@code container} is null
433      */
434     public static <T extends ChildOf<? extends DataRoot>> @NonNull InstanceIdentifierBuilder<T> builder(
435             final Class<T> container) {
436         return new InstanceIdentifierBuilderImpl<T>().addWildNode(Item.of(container));
437     }
438
439     /**
440      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container in
441      * a {@code grouping} used in the {@code case} statement.
442      *
443      * @param caze Choice case class
444      * @param container Base container
445      * @param <C> Case type
446      * @param <T> Type of the container
447      * @return A new {@link InstanceIdentifierBuilder}
448      * @throws NullPointerException if any argument is null
449      */
450     public static <C extends ChoiceIn<? extends DataRoot> & DataObject, T extends ChildOf<? super C>>
451             @NonNull InstanceIdentifierBuilder<T> builder(final Class<C> caze, final Class<T> container) {
452         return new InstanceIdentifierBuilderImpl<T>().addWildNode(Item.of(caze, container));
453     }
454
455     /**
456      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an
457      * {@link IdentifiableItem}.
458      *
459      * @param listItem list item class
460      * @param listKey key value
461      * @param <N> List type
462      * @param <K> List key
463      * @return A new {@link InstanceIdentifierBuilder}
464      * @throws NullPointerException if any argument is null
465      */
466     public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>,
467             K extends Identifier<N>> @NonNull InstanceIdentifierBuilder<N> builder(final Class<N> listItem,
468                     final K listKey) {
469         return new InstanceIdentifierBuilderImpl<N>().addNode(IdentifiableItem.of(listItem, listKey));
470     }
471
472     /**
473      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an
474      * {@link IdentifiableItem} in a {@code grouping} used in the {@code case} statement.
475      *
476      * @param caze Choice case class
477      * @param listItem list item class
478      * @param listKey key value
479      * @param <C> Case type
480      * @param <N> List type
481      * @param <K> List key
482      * @return A new {@link InstanceIdentifierBuilder}
483      * @throws NullPointerException if any argument is null
484      */
485     public static <C extends ChoiceIn<? extends DataRoot> & DataObject,
486             N extends Identifiable<K> & ChildOf<? super C>, K extends Identifier<N>>
487             @NonNull InstanceIdentifierBuilder<N> builder(final Class<C> caze, final Class<N> listItem,
488                     final K listKey) {
489         return new InstanceIdentifierBuilderImpl<N>().addNode(IdentifiableItem.of(caze, listItem, listKey));
490     }
491
492     /**
493      * Create an instance identifier for a very specific object type. This method implements {@link #create(Iterable)}
494      * semantics, except it is used by internal callers, which have assured that the argument is an immutable Iterable.
495      *
496      * @param pathArguments The path to a specific node in the data tree
497      * @return InstanceIdentifier instance
498      * @throws IllegalArgumentException if pathArguments is empty or contains a null element.
499      * @throws NullPointerException if {@code pathArguments} is null
500      */
501     private static @NonNull InstanceIdentifier<?> internalCreate(final Iterable<PathArgument> pathArguments) {
502         final Iterator<? extends PathArgument> it = requireNonNull(pathArguments, "pathArguments may not be null")
503                 .iterator();
504         final HashCodeBuilder<PathArgument> hashBuilder = new HashCodeBuilder<>();
505         boolean wildcard = false;
506         PathArgument arg = null;
507
508         while (it.hasNext()) {
509             arg = it.next();
510             checkArgument(arg != null, "pathArguments may not contain null elements");
511
512             // TODO: sanity check ChildOf<>;
513             hashBuilder.addArgument(arg);
514
515             if (Identifiable.class.isAssignableFrom(arg.getType()) && !(arg instanceof IdentifiableItem<?, ?>)) {
516                 wildcard = true;
517             }
518         }
519         checkArgument(arg != null, "pathArguments may not be empty");
520
521         return trustedCreate(arg, pathArguments, hashBuilder.build(), wildcard);
522     }
523
524     /**
525      * Create an instance identifier for a very specific object type.
526      *
527      * <p>
528      * Example:
529      * <pre>
530      *  List&lt;PathArgument&gt; path = Arrays.asList(new Item(Nodes.class))
531      *  new InstanceIdentifier(path);
532      * </pre>
533      *
534      * @param pathArguments The path to a specific node in the data tree
535      * @return InstanceIdentifier instance
536      * @throws IllegalArgumentException if pathArguments is empty or
537      *         contains a null element.
538      */
539     public static @NonNull InstanceIdentifier<?> create(final Iterable<? extends PathArgument> pathArguments) {
540         if (pathArguments instanceof ImmutableCollection) {
541             @SuppressWarnings("unchecked")
542             final var immutableArguments = (ImmutableCollection<PathArgument>) pathArguments;
543             return internalCreate(immutableArguments);
544         }
545
546         return internalCreate(ImmutableList.copyOf(pathArguments));
547     }
548
549     /**
550      * Create an instance identifier for a very specific object type.
551      *
552      * <p>
553      * For example
554      * <pre>
555      *      new InstanceIdentifier(Nodes.class)
556      * </pre>
557      * would create an InstanceIdentifier for an object of type Nodes
558      *
559      * @param type The type of the object which this instance identifier represents
560      * @return InstanceIdentifier instance
561      */
562     @SuppressWarnings("unchecked")
563     public static <T extends DataObject> @NonNull InstanceIdentifier<T> create(final Class<@NonNull T> type) {
564         return (InstanceIdentifier<T>) create(ImmutableList.of(Item.of(type)));
565     }
566
567     /**
568      * Return the key associated with the last component of the specified identifier.
569      *
570      * @param id instance identifier
571      * @return key associated with the last component
572      * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
573      * @throws NullPointerException if id is null.
574      */
575     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(
576             final InstanceIdentifier<N> id) {
577         requireNonNull(id);
578         checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
579
580         @SuppressWarnings("unchecked")
581         final K ret = ((KeyedInstanceIdentifier<N, K>)id).getKey();
582         return ret;
583     }
584
585     @SuppressWarnings({ "unchecked", "rawtypes" })
586     static <N extends DataObject> @NonNull InstanceIdentifier<N> trustedCreate(final PathArgument arg,
587             final Iterable<PathArgument> pathArguments, final int hash, boolean wildcarded) {
588         if (Identifiable.class.isAssignableFrom(arg.getType()) && !wildcarded) {
589             Identifier<?> key = null;
590             if (arg instanceof IdentifiableItem) {
591                 key = ((IdentifiableItem<?, ?>)arg).getKey();
592             } else {
593                 wildcarded = true;
594             }
595
596             return new KeyedInstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash, key);
597         }
598
599         return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash);
600     }
601
602     /**
603      * Path argument of {@link InstanceIdentifier}. Interface which implementations are used as path components of the
604      * path in overall data tree.
605      */
606     public interface PathArgument extends Comparable<PathArgument> {
607         /**
608          * Return the data object type backing this PathArgument.
609          *
610          * @return Data object type.
611          */
612         @NonNull Class<? extends DataObject> getType();
613
614         /**
615          * Return an optional enclosing case type. This is used only when {@link #getType()} references a node defined
616          * in a {@code grouping} which is reference inside a {@code case} statement in order to safely reference the
617          * node.
618          *
619          * @return Optional case class.
620          */
621         default Optional<? extends Class<? extends DataObject>> getCaseType() {
622             return Optional.empty();
623         }
624     }
625
626     private abstract static class AbstractPathArgument<T extends DataObject> implements PathArgument, Serializable {
627         private static final long serialVersionUID = 1L;
628
629         private final @NonNull Class<T> type;
630
631         AbstractPathArgument(final Class<T> type) {
632             this.type = requireNonNull(type, "Type may not be null.");
633         }
634
635         @Override
636         public final Class<T> getType() {
637             return type;
638         }
639
640         Object getKey() {
641             return null;
642         }
643
644         @Override
645         public final int hashCode() {
646             return Objects.hash(type, getCaseType(), getKey());
647         }
648
649         @Override
650         public final boolean equals(final Object obj) {
651             if (this == obj) {
652                 return true;
653             }
654             if (!(obj instanceof AbstractPathArgument)) {
655                 return false;
656             }
657             final AbstractPathArgument<?> other = (AbstractPathArgument<?>) obj;
658             return type.equals(other.type) && Objects.equals(getKey(), other.getKey())
659                     && getCaseType().equals(other.getCaseType());
660         }
661
662         @Override
663         public final int compareTo(final PathArgument arg) {
664             final int cmp = compareClasses(type, arg.getType());
665             if (cmp != 0) {
666                 return cmp;
667             }
668             final Optional<? extends Class<?>> caseType = getCaseType();
669             if (!caseType.isPresent()) {
670                 return arg.getCaseType().isPresent() ? -1 : 1;
671             }
672             final Optional<? extends Class<?>> argCaseType = getCaseType();
673             return argCaseType.isPresent() ? compareClasses(caseType.get(), argCaseType.get()) : 1;
674         }
675
676         private static int compareClasses(final Class<?> first, final Class<?> second) {
677             return first.getCanonicalName().compareTo(second.getCanonicalName());
678         }
679     }
680
681     /**
682      * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
683      * a kind. In YANG terms this would probably represent a container.
684      *
685      * @param <T> Item type
686      */
687     public static class Item<T extends DataObject> extends AbstractPathArgument<T> {
688         private static final long serialVersionUID = 1L;
689
690         Item(final Class<T> type) {
691             super(type);
692         }
693
694         /**
695          * Return a PathArgument instance backed by the specified class.
696          *
697          * @param type Backing class
698          * @param <T> Item type
699          * @return A new PathArgument
700          * @throws NullPointerException if {@code} is null.
701          */
702         public static <T extends DataObject> @NonNull Item<T> of(final Class<T> type) {
703             return new Item<>(type);
704         }
705
706         /**
707          * Return a PathArgument instance backed by the specified class, which in turn is defined in a {@code grouping}
708          * used in a corresponding {@code case} statement.
709          *
710          * @param caseType defining case class
711          * @param type Backing class
712          * @param <C> Case type
713          * @param <T> Item type
714          * @return A new PathArgument
715          * @throws NullPointerException if any argument is null.
716          */
717         public static <C extends ChoiceIn<?> & DataObject, T extends ChildOf<? super C>> @NonNull Item<T> of(
718                 final Class<C> caseType, final Class<T> type) {
719             return new CaseItem<>(caseType, type);
720         }
721
722         @Override
723         public String toString() {
724             return getType().getName();
725         }
726     }
727
728     /**
729      * An IdentifiableItem represents a object that is usually present in a collection and can be identified uniquely
730      * by a key. In YANG terms this would probably represent an item in a list.
731      *
732      * @param <I> An object that is identifiable by an identifier
733      * @param <T> The identifier of the object
734      */
735     public static class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>>
736             extends AbstractPathArgument<I> {
737         private static final long serialVersionUID = 1L;
738
739         private final @NonNull T key;
740
741         IdentifiableItem(final Class<I> type, final T key) {
742             super(type);
743             this.key = requireNonNull(key, "Key may not be null.");
744         }
745
746         /**
747          * Return an IdentifiableItem instance backed by the specified class with specified key.
748          *
749          * @param type Backing class
750          * @param key Key
751          * @param <T> List type
752          * @param <I> Key type
753          * @return An IdentifiableItem
754          * @throws NullPointerException if any argument is null.
755          */
756         public static <T extends Identifiable<I> & DataObject, I extends Identifier<T>>
757                 @NonNull IdentifiableItem<T, I> of(final Class<T> type, final I key) {
758             return new IdentifiableItem<>(type, key);
759         }
760
761         /**
762          * Return an IdentifiableItem instance backed by the specified class with specified key. The class is in turn
763          * defined in a {@code grouping} used in a corresponding {@code case} statement.
764          *
765          * @param caseType defining case class
766          * @param type Backing class
767          * @param <C> Case type
768          * @param <T> List type
769          * @param <I> Key type
770          * @return A new PathArgument
771          * @throws NullPointerException if any argument is null.
772          */
773         public static <C extends ChoiceIn<?> & DataObject, T extends ChildOf<? super C> & Identifiable<I>,
774                 I extends Identifier<T>> @NonNull IdentifiableItem<T, I> of(final Class<C> caseType,
775                         final Class<T> type, final I key) {
776             return new CaseIdentifiableItem<>(caseType, type, key);
777         }
778
779         /**
780          * Return the data object type backing this PathArgument.
781          *
782          * @return Data object type.
783          */
784         @Override
785         public final @NonNull T getKey() {
786             return key;
787         }
788
789         @Override
790         public String toString() {
791             return getType().getName() + "[key=" + key + "]";
792         }
793     }
794
795     private static final class CaseItem<C extends ChoiceIn<?> & DataObject, T extends ChildOf<? super C>>
796             extends Item<T> {
797         private static final long serialVersionUID = 1L;
798
799         private final Class<C> caseType;
800
801         CaseItem(final Class<C> caseType, final Class<T> type) {
802             super(type);
803             this.caseType = requireNonNull(caseType);
804         }
805
806         @Override
807         public Optional<Class<C>> getCaseType() {
808             return Optional.of(caseType);
809         }
810     }
811
812     private static final class CaseIdentifiableItem<C extends ChoiceIn<?> & DataObject,
813             T extends ChildOf<? super C> & Identifiable<K>, K extends Identifier<T>> extends IdentifiableItem<T, K> {
814         private static final long serialVersionUID = 1L;
815
816         private final Class<C> caseType;
817
818         CaseIdentifiableItem(final Class<C> caseType, final Class<T> type, final K key) {
819             super(type, key);
820             this.caseType = requireNonNull(caseType);
821         }
822
823         @Override
824         public Optional<Class<C>> getCaseType() {
825             return Optional.of(caseType);
826         }
827     }
828
829     public interface InstanceIdentifierBuilder<T extends DataObject> {
830         /**
831          * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This
832          * method should be used when you want to build an instance identifier by appending top-level elements, for
833          * example
834          * <pre>
835          *     InstanceIdentifier.builder().child(Nodes.class).build();
836          * </pre>
837          *
838          * <p>
839          * NOTE :- The above example is only for illustration purposes InstanceIdentifier.builder() has been deprecated
840          * and should not be used. Use InstanceIdentifier.builder(Nodes.class) instead
841          *
842          * @param container Container to append
843          * @param <N> Container type
844          * @return this builder
845          * @throws NullPointerException if {@code container} is null
846          */
847         <N extends ChildOf<? super T>> @NonNull InstanceIdentifierBuilder<N> child(Class<N> container);
848
849         /**
850          * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This
851          * method should be used when you want to build an instance identifier by appending a container node to the
852          * identifier and the {@code container} is defined in a {@code grouping} used in a {@code case} statement.
853          *
854          * @param caze Choice case class
855          * @param container Container to append
856          * @param <C> Case type
857          * @param <N> Container type
858          * @return this builder
859          * @throws NullPointerException if {@code container} is null
860          */
861         <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
862                 @NonNull InstanceIdentifierBuilder<N> child(Class<C> caze, Class<N> container);
863
864         /**
865          * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This
866          * method should be used when you want to build an instance identifier by appending a specific list element to
867          * the identifier.
868          *
869          * @param listItem List to append
870          * @param listKey List key
871          * @param <N> List type
872          * @param <K> Key type
873          * @return this builder
874          * @throws NullPointerException if any argument is null
875          */
876         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>>
877                 @NonNull InstanceIdentifierBuilder<N> child(Class<@NonNull N> listItem, K listKey);
878
879         /**
880          * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This
881          * method should be used when you want to build an instance identifier by appending a specific list element to
882          * the identifier and the {@code list} is defined in a {@code grouping} used in a {@code case} statement.
883          *
884          * @param caze Choice case class
885          * @param listItem List to append
886          * @param listKey List key
887          * @param <C> Case type
888          * @param <N> List type
889          * @param <K> Key type
890          * @return this builder
891          * @throws NullPointerException if any argument is null
892          */
893         <C extends ChoiceIn<? super T> & DataObject, K extends Identifier<N>,
894                 N extends Identifiable<K> & ChildOf<? super C>> @NonNull InstanceIdentifierBuilder<N> child(
895                         Class<C> caze, Class<N> listItem, K listKey);
896
897         /**
898          * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by
899          * the builder.
900          *
901          * @param container augmentation class
902          * @param <N> augmentation type
903          * @return this builder
904          * @throws NullPointerException if {@code container} is null
905          */
906         <N extends DataObject & Augmentation<? super T>> @NonNull InstanceIdentifierBuilder<N> augmentation(
907                 Class<N> container);
908
909         /**
910          * Build the instance identifier.
911          *
912          * @return Resulting instance identifier.
913          */
914         @NonNull InstanceIdentifier<T> build();
915     }
916
917     private Object writeReplace() throws ObjectStreamException {
918         return new InstanceIdentifierV3<>(this);
919     }
920 }