Add DataObject{Identifier,Reference}.Builder(.WithKey)
[yangtools.git] / binding / binding-spec / 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 com.google.common.base.Verify.verifyNotNull;
13 import static java.util.Objects.requireNonNull;
14
15 import com.google.common.base.VerifyException;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.Iterables;
18 import java.io.IOException;
19 import java.io.ObjectInputStream;
20 import java.io.ObjectOutputStream;
21 import java.io.ObjectStreamException;
22 import java.io.Serializable;
23 import java.util.List;
24 import java.util.Objects;
25 import org.eclipse.jdt.annotation.NonNull;
26 import org.eclipse.jdt.annotation.Nullable;
27 import org.opendaylight.yangtools.binding.Augmentable;
28 import org.opendaylight.yangtools.binding.Augmentation;
29 import org.opendaylight.yangtools.binding.ChildOf;
30 import org.opendaylight.yangtools.binding.ChoiceIn;
31 import org.opendaylight.yangtools.binding.DataObject;
32 import org.opendaylight.yangtools.binding.DataObjectIdentifier;
33 import org.opendaylight.yangtools.binding.DataObjectReference;
34 import org.opendaylight.yangtools.binding.DataObjectStep;
35 import org.opendaylight.yangtools.binding.DataRoot;
36 import org.opendaylight.yangtools.binding.ExactDataObjectStep;
37 import org.opendaylight.yangtools.binding.Key;
38 import org.opendaylight.yangtools.binding.KeyAware;
39 import org.opendaylight.yangtools.binding.KeyStep;
40 import org.opendaylight.yangtools.binding.KeylessStep;
41 import org.opendaylight.yangtools.binding.NodeStep;
42 import org.opendaylight.yangtools.binding.impl.AbstractDataObjectReference;
43 import org.opendaylight.yangtools.binding.impl.AbstractDataObjectReferenceBuilder;
44 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
45
46 /**
47  * This instance identifier uniquely identifies a specific DataObject in the data tree modeled by YANG.
48  *
49  * <p>
50  * For Example let's say you were trying to refer to a node in inventory which was modeled in YANG as follows,
51  * <pre>code{
52  *   module opendaylight-inventory {
53  *     ....
54  *
55  *     container nodes {
56  *       list node {
57  *         key "id";
58  *         ext:context-instance "node-context";
59  *
60  *         uses node;
61  *       }
62  *     }
63  *   }
64  * }</pre>
65  *
66  * <p>
67  * You can create an instance identifier as follows to get to a node with id "openflow:1": {@code
68  * InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build();
69  * }
70  *
71  * <p>
72  * This would be the same as using a path like so, "/nodes/node/openflow:1" to refer to the openflow:1 node
73  */
74 public sealed class InstanceIdentifier<T extends DataObject> extends AbstractDataObjectReference<T, DataObjectStep<?>>
75         implements HierarchicalIdentifier<InstanceIdentifier<? extends DataObject>>
76         permits KeyedInstanceIdentifier {
77     @java.io.Serial
78     private static final long serialVersionUID = 3L;
79
80     private final boolean wildcarded;
81
82     InstanceIdentifier(final Iterable<? extends @NonNull DataObjectStep<?>> steps, final boolean wildcarded) {
83         super(steps);
84         this.wildcarded = wildcarded;
85     }
86
87     /**
88      * Return the type of data which this InstanceIdentifier identifies.
89      *
90      * @return Target type
91      */
92     public final @NonNull Class<T> getTargetType() {
93         return lastStep().type();
94     }
95
96     /**
97      * Perform a safe target type adaptation of this instance identifier to target type. This method is useful when
98      * dealing with type-squashed instances.
99      *
100      * @return Path argument with target type
101      * @throws VerifyException if this instance identifier cannot be adapted to target type
102      * @throws NullPointerException if {@code target} is null
103      */
104     @SuppressWarnings("unchecked")
105     public final <N extends DataObject> @NonNull InstanceIdentifier<N> verifyTarget(final Class<@NonNull N> target) {
106         verify(target.equals(getTargetType()), "Cannot adapt %s to %s", this, target);
107         return (InstanceIdentifier<N>) this;
108     }
109
110     @Override
111     public final boolean isExact() {
112         return !wildcarded;
113     }
114
115     @Override
116     @Deprecated(since = "14.0.0", forRemoval = true)
117     public final boolean isWildcarded() {
118         return wildcarded;
119     }
120
121     @Override
122     public final InstanceIdentifier<T> toLegacy() {
123         return this;
124     }
125
126     @Override
127     protected final Class<?> contract() {
128         return wildcarded ? super.contract() : DataObjectIdentifier.class;
129     }
130
131     /**
132      * Return an instance identifier trimmed at the first occurrence of a specific component type.
133      *
134      * <p>
135      * For example let's say an instance identifier was built like so,
136      * <pre>
137      *      identifier = InstanceIdentifier.builder(Nodes.class).child(Node.class,
138      *                   new NodeKey(new NodeId("openflow:1")).build();
139      * </pre>
140      *
141      * <p>
142      * And you wanted to obtain the Instance identifier which represented Nodes you would do it like so,
143      * <pre>
144      *      identifier.firstIdentifierOf(Nodes.class)
145      * </pre>
146      *
147      * @param type component type
148      * @return trimmed instance identifier, or null if the component type
149      *         is not present.
150      */
151     public final <I extends DataObject> @Nullable InstanceIdentifier<I> firstIdentifierOf(
152             final Class<@NonNull I> type) {
153         int count = 1;
154         for (var step : steps()) {
155             if (type.equals(step.type())) {
156                 @SuppressWarnings("unchecked")
157                 final var ret = (InstanceIdentifier<I>) internalCreate(Iterables.limit(steps(), count));
158                 return ret;
159             }
160
161             ++count;
162         }
163
164         return null;
165     }
166
167     /**
168      * Return the key associated with the first component of specified type in
169      * an identifier.
170      *
171      * @param listItem component type
172      * @return key associated with the component, or null if the component type
173      *         is not present.
174      */
175     public final <N extends KeyAware<K> & DataObject, K extends Key<N>> @Nullable K firstKeyOf(
176             final Class<@NonNull N> listItem) {
177         for (var step : steps()) {
178             if (step instanceof KeyStep<?, ?> keyPredicate && listItem.equals(step.type())) {
179                 @SuppressWarnings("unchecked")
180                 final var ret = (K) keyPredicate.key();
181                 return ret;
182             }
183         }
184         return null;
185     }
186
187     /**
188      * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all
189      * PathArguments to match exactly.
190      *
191      * <p>
192      * The contains method checks if the other identifier is fully contained within the current identifier. It does this
193      * by looking at only the types of the path arguments and not by comparing the path arguments themselves.
194      *
195      * <p>
196      * To illustrate here is an example which explains the working of this API. Let's say you have two instance
197      * identifiers as follows:
198      * {@code
199      * this = /nodes/node/openflow:1
200      * other = /nodes/node/openflow:2
201      * }
202      * then this.contains(other) will return false.
203      *
204      * @param other Potentially-container instance identifier
205      * @return True if the specified identifier is contained in this identifier.
206      */
207     @Override
208     public final boolean contains(final InstanceIdentifier<? extends DataObject> other) {
209         requireNonNull(other, "other should not be null");
210
211         final var oit = other.steps().iterator();
212         for (var step : steps()) {
213             if (!oit.hasNext()) {
214                 return false;
215             }
216             if (!step.equals(oit.next())) {
217                 return false;
218             }
219         }
220         return true;
221     }
222
223     /**
224      * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar
225      * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their
226      * non-wildcarded PathArgument counterpart.
227      *
228      * @param other Identifier which should be checked for inclusion.
229      * @return true if this identifier contains the other object
230      */
231     public final boolean containsWildcarded(final InstanceIdentifier<?> other) {
232         requireNonNull(other, "other should not be null");
233
234         final var otherSteps = other.steps().iterator();
235         for (var step : steps()) {
236             if (!otherSteps.hasNext()) {
237                 return false;
238             }
239
240             final var otherStep = otherSteps.next();
241             if (step instanceof ExactDataObjectStep) {
242                 if (!step.equals(otherStep)) {
243                     return false;
244                 }
245             } else if (step instanceof KeylessStep<?> keyless) {
246                 if (!keyless.matches(otherStep)) {
247                     return false;
248                 }
249             } else {
250                 throw new IllegalStateException("Unhandled step " + step);
251             }
252         }
253
254         return true;
255     }
256
257     private <N extends DataObject> @NonNull InstanceIdentifier<N> childIdentifier(final DataObjectStep<N> arg) {
258         return trustedCreate(arg, concat(steps(), arg), wildcarded);
259     }
260
261     /**
262      * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
263      * {@code builder().child(container).build()}.
264      *
265      * @param container Container to append
266      * @param <N> Container type
267      * @return An InstanceIdentifier.
268      * @throws NullPointerException if {@code container} is null
269      */
270     public final <N extends ChildOf<? super T>> @NonNull InstanceIdentifier<N> child(
271             final Class<@NonNull N> container) {
272         return childIdentifier(DataObjectStep.of(container));
273     }
274
275     /**
276      * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
277      * {@code builder().child(listItem, listKey).build()}.
278      *
279      * @param listItem List to append
280      * @param listKey List key
281      * @param <N> List type
282      * @param <K> Key type
283      * @return An InstanceIdentifier.
284      * @throws NullPointerException if any argument is null
285      */
286     @SuppressWarnings("unchecked")
287     public final <N extends KeyAware<K> & ChildOf<? super T>, K extends Key<N>>
288             @NonNull KeyedInstanceIdentifier<N, K> child(final Class<@NonNull N> listItem, final K listKey) {
289         return (KeyedInstanceIdentifier<N, K>) childIdentifier(new KeyStep<>(listItem, listKey));
290     }
291
292     /**
293      * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
294      * {@code builder().child(caze, container).build()}.
295      *
296      * @param caze Choice case class
297      * @param container Container to append
298      * @param <C> Case type
299      * @param <N> Container type
300      * @return An InstanceIdentifier.
301      * @throws NullPointerException if any argument is null
302      */
303     // FIXME: add a proper caller
304     public final <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
305             @NonNull InstanceIdentifier<N> child(final Class<@NonNull C> caze, final Class<@NonNull N> container) {
306         return childIdentifier(DataObjectStep.of(caze, container));
307     }
308
309     /**
310      * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
311      * {@code builder().child(caze, listItem, listKey).build()}.
312      *
313      * @param caze Choice case class
314      * @param listItem List to append
315      * @param listKey List key
316      * @param <C> Case type
317      * @param <N> List type
318      * @param <K> Key type
319      * @return An InstanceIdentifier.
320      * @throws NullPointerException if any argument is null
321      */
322     // FIXME: add a proper caller
323     @SuppressWarnings("unchecked")
324     public final <C extends ChoiceIn<? super T> & DataObject, K extends Key<N>,
325         N extends KeyAware<K> & ChildOf<? super C>> @NonNull KeyedInstanceIdentifier<N, K> child(
326                 final Class<@NonNull C> caze, final Class<@NonNull N> listItem, final K listKey) {
327         return (KeyedInstanceIdentifier<N, K>) childIdentifier(new KeyStep<>(listItem, requireNonNull(caze), listKey));
328     }
329
330     /**
331      * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to
332      * {@code builder().augmentation(container).build()}.
333      *
334      * @param container Container to append
335      * @param <N> Container type
336      * @return An InstanceIdentifier.
337      * @throws NullPointerException if {@code container} is null
338      */
339     public final <N extends DataObject & Augmentation<? super T>> @NonNull InstanceIdentifier<N> augmentation(
340             final Class<@NonNull N> container) {
341         return childIdentifier(new NodeStep<>(container));
342     }
343
344     @Override
345     protected Object toSerialForm() {
346         return new IIv5(this);
347     }
348
349     @java.io.Serial
350     private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
351         throwNSE();
352     }
353
354     @java.io.Serial
355     private void readObjectNoData() throws ObjectStreamException {
356         throwNSE();
357     }
358
359     @java.io.Serial
360     private void writeObject(final ObjectOutputStream stream) throws IOException {
361         throwNSE();
362     }
363
364     @Override
365     public Builder<T> toBuilder() {
366         return new RegularBuilder<>(this);
367     }
368
369     /**
370      * Create a {@link Builder} for a specific type of InstanceIdentifier as specified by container.
371      *
372      * @param container Base container
373      * @param <T> Type of the container
374      * @return A new {@link Builder}
375      * @throws NullPointerException if {@code container} is null
376      */
377     public static <T extends ChildOf<? extends DataRoot>> @NonNull Builder<T> builder(
378             final @NonNull Class<T> container) {
379         return new RegularBuilder<>(DataObjectStep.of(container));
380     }
381
382     /**
383      * Create a {@link Builder} for a specific type of InstanceIdentifier as specified by container in
384      * a {@code grouping} used in the {@code case} statement.
385      *
386      * @param caze Choice case class
387      * @param container Base container
388      * @param <C> Case type
389      * @param <T> Type of the container
390      * @return A new {@link Builder}
391      * @throws NullPointerException if any argument is null
392      */
393     public static <C extends ChoiceIn<? extends DataRoot> & DataObject, T extends ChildOf<? super C>>
394             @NonNull Builder<T> builder(final @NonNull Class<C> caze, final @NonNull Class<T> container) {
395         return new RegularBuilder<>(DataObjectStep.of(caze, container));
396     }
397
398     /**
399      * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link IdentifiableItem}.
400      *
401      * @param listItem list item class
402      * @param listKey key value
403      * @param <N> List type
404      * @param <K> List key
405      * @return A new {@link Builder}
406      * @throws NullPointerException if any argument is null
407      */
408     public static <N extends KeyAware<K> & ChildOf<? extends DataRoot>,
409             K extends Key<N>> @NonNull KeyedBuilder<N, K> builder(final Class<N> listItem,
410                     final K listKey) {
411         return new KeyedBuilder<>(new KeyStep<>(listItem, listKey));
412     }
413
414     /**
415      * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link IdentifiableItem}
416      *  in a {@code grouping} used in the {@code case} statement.
417      *
418      * @param caze Choice case class
419      * @param listItem list item class
420      * @param listKey key value
421      * @param <C> Case type
422      * @param <N> List type
423      * @param <K> List key
424      * @return A new {@link Builder}
425      * @throws NullPointerException if any argument is null
426      */
427     public static <C extends ChoiceIn<? extends DataRoot> & DataObject,
428             N extends KeyAware<K> & ChildOf<? super C>, K extends Key<N>>
429             @NonNull KeyedBuilder<N, K> builder(final @NonNull Class<C> caze, final @NonNull Class<N> listItem,
430                     final @NonNull K listKey) {
431         return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey));
432     }
433
434     public static <R extends DataRoot & DataObject, T extends ChildOf<? super R>>
435             @NonNull Builder<T> builderOfInherited(final @NonNull Class<R> root, final @NonNull Class<T> container) {
436         // FIXME: we are losing root identity, hence namespaces may not work correctly
437         return new RegularBuilder<>(DataObjectStep.of(container));
438     }
439
440     public static <R extends DataRoot & DataObject, C extends ChoiceIn<? super R> & DataObject,
441             T extends ChildOf<? super C>>
442             @NonNull Builder<T> builderOfInherited(final Class<R> root,
443                 final Class<C> caze, final Class<T> container) {
444         // FIXME: we are losing root identity, hence namespaces may not work correctly
445         return new RegularBuilder<>(DataObjectStep.of(caze, container));
446     }
447
448     public static <R extends DataRoot & DataObject, N extends KeyAware<K> & ChildOf<? super R>,
449             K extends Key<N>>
450             @NonNull KeyedBuilder<N, K> builderOfInherited(final @NonNull Class<R> root,
451                 final @NonNull Class<N> listItem, final @NonNull K listKey) {
452         // FIXME: we are losing root identity, hence namespaces may not work correctly
453         return new KeyedBuilder<>(new KeyStep<>(listItem, listKey));
454     }
455
456     public static <R extends DataRoot & DataObject, C extends ChoiceIn<? super R> & DataObject,
457             N extends KeyAware<K> & ChildOf<? super C>, K extends Key<N>>
458             @NonNull KeyedBuilder<N, K> builderOfInherited(final Class<R> root,
459                 final Class<C> caze, final Class<N> listItem, final K listKey) {
460         // FIXME: we are losing root identity, hence namespaces may not work correctly
461         return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey));
462     }
463
464     /**
465      * Create an instance identifier for a very specific object type. This method implements {@link #create(Iterable)}
466      * semantics, except it is used by internal callers, which have assured that the argument is an immutable Iterable.
467      *
468      * @param pathArguments The path to a specific node in the data tree
469      * @return InstanceIdentifier instance
470      * @throws IllegalArgumentException if pathArguments is empty or contains a null element.
471      * @throws NullPointerException if {@code pathArguments} is null
472      */
473     private static @NonNull InstanceIdentifier<?> internalCreate(
474             final Iterable<? extends DataObjectStep<?>> pathArguments) {
475         final var it = requireNonNull(pathArguments, "pathArguments may not be null").iterator();
476         checkArgument(it.hasNext(), "pathArguments may not be empty");
477
478         boolean wildcard = false;
479         DataObjectStep<?> arg;
480
481         do {
482             arg = it.next();
483             // Non-null is implied by our callers
484             final var type = verifyNotNull(arg).type();
485             checkArgument(ChildOf.class.isAssignableFrom(type) || Augmentation.class.isAssignableFrom(type),
486                 "%s is not a valid path argument", type);
487
488             if (!(arg instanceof ExactDataObjectStep)) {
489                 wildcard = true;
490             }
491         } while (it.hasNext());
492
493         return trustedCreate(arg, pathArguments, wildcard);
494     }
495
496     /**
497      * Create an instance identifier for a sequence of {@link DataObjectStep} steps. The steps are required to be formed
498      * of classes extending either {@link ChildOf} or {@link Augmentation} contracts. This method does not check whether
499      * or not the sequence is structurally sound, for example that an {@link Augmentation} follows an
500      * {@link Augmentable} step. Furthermore the compile-time indicated generic type of the returned object does not
501      * necessarily match the contained state.
502      *
503      * <p>
504      * Failure to observe precautions to validate the list's contents may yield an object which mey be rejected at
505      * run-time or lead to undefined behaviour.
506      *
507      * @param pathArguments The path to a specific node in the data tree
508      * @return InstanceIdentifier instance
509      * @throws NullPointerException if {@code pathArguments} is, or contains an item which is, {@code null}
510      * @throws IllegalArgumentException if {@code pathArguments} is empty or contains an item which does not represent
511      *                                  a valid addressing step.
512      */
513     @SuppressWarnings("unchecked")
514     public static <T extends DataObject> @NonNull InstanceIdentifier<T> unsafeOf(
515             final List<? extends DataObjectStep<?>> pathArguments) {
516         return (InstanceIdentifier<T>) internalCreate(ImmutableList.copyOf(pathArguments));
517     }
518
519     /**
520      * Create an instance identifier for a very specific object type.
521      *
522      * <p>
523      * For example
524      * <pre>
525      *      new InstanceIdentifier(Nodes.class)
526      * </pre>
527      * would create an InstanceIdentifier for an object of type Nodes
528      *
529      * @param type The type of the object which this instance identifier represents
530      * @return InstanceIdentifier instance
531      */
532     // FIXME: considering removing in favor of always going through a builder
533     @SuppressWarnings("unchecked")
534     public static <T extends ChildOf<? extends DataRoot>> @NonNull InstanceIdentifier<T> create(
535             final Class<@NonNull T> type) {
536         return (InstanceIdentifier<T>) internalCreate(ImmutableList.of(DataObjectStep.of(type)));
537     }
538
539     /**
540      * Return the key associated with the last component of the specified identifier.
541      *
542      * @param id instance identifier
543      * @return key associated with the last component
544      * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
545      * @throws NullPointerException if id is null.
546      */
547     // FIXME: reconsider naming and design of this method
548     public static <N extends KeyAware<K> & DataObject, K extends Key<N>> K keyOf(
549             final InstanceIdentifier<N> id) {
550         requireNonNull(id);
551         checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
552
553         @SuppressWarnings("unchecked")
554         final K ret = ((KeyedInstanceIdentifier<N, K>)id).key();
555         return ret;
556     }
557
558     @SuppressWarnings({ "unchecked", "rawtypes" })
559     static <N extends DataObject> @NonNull InstanceIdentifier<N> trustedCreate(final DataObjectStep<?> lastStep,
560             final Iterable<? extends DataObjectStep<?>> pathArguments, final boolean wildcarded) {
561         return switch (lastStep) {
562             case NodeStep<?> cast -> new InstanceIdentifier(pathArguments, wildcarded);
563             case KeyStep<?, ?> cast -> new KeyedInstanceIdentifier(pathArguments, wildcarded);
564             case KeylessStep<?> cast -> new InstanceIdentifier(pathArguments, true);
565         };
566     }
567
568     @Deprecated(since = "13.0.0", forRemoval = true)
569     private abstract static sealed class AbstractPathArgument<T extends DataObject>
570             implements Comparable<AbstractPathArgument<?>>, Serializable {
571         @java.io.Serial
572         private static final long serialVersionUID = 1L;
573
574         private final @NonNull Class<T> type;
575
576         AbstractPathArgument(final Class<T> type) {
577             this.type = requireNonNull(type, "Type may not be null.");
578         }
579
580         /**
581          * Return the data object type backing this PathArgument.
582          *
583          * @return Data object type.
584          */
585         final @NonNull Class<T> type() {
586             return type;
587         }
588
589         /**
590          * Return an optional enclosing case type. This is used only when {@link #type()} references a node defined
591          * in a {@code grouping} which is reference inside a {@code case} statement in order to safely reference the
592          * node.
593          *
594          * @return case class or {@code null}
595          */
596         Class<? extends DataObject> caseType() {
597             return null;
598         }
599
600         @Nullable Object key() {
601             return null;
602         }
603
604         @Override
605         public final int hashCode() {
606             return Objects.hash(type, caseType(), key());
607         }
608
609         @Override
610         public final boolean equals(final Object obj) {
611             return this == obj || obj instanceof AbstractPathArgument<?> other && type.equals(other.type)
612                 && Objects.equals(key(), other.key()) && Objects.equals(caseType(), other.caseType());
613         }
614
615         @Override
616         public final int compareTo(final AbstractPathArgument<?> arg) {
617             final int cmp = compareClasses(type, arg.type());
618             if (cmp != 0) {
619                 return cmp;
620             }
621             final var caseType = caseType();
622             final var argCaseType = arg.caseType();
623             if (caseType == null) {
624                 return argCaseType == null ? 1 : -1;
625             }
626             return argCaseType == null ? 1 : compareClasses(caseType, argCaseType);
627         }
628
629         private static int compareClasses(final Class<?> first, final Class<?> second) {
630             return first.getCanonicalName().compareTo(second.getCanonicalName());
631         }
632
633         @java.io.Serial
634         final Object readResolve() throws ObjectStreamException {
635             return toStep();
636         }
637
638         abstract DataObjectStep<?> toStep();
639     }
640
641     /**
642      * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
643      * a kind. In YANG terms this would probably represent a container.
644      *
645      * @param <T> Item type
646      */
647     @Deprecated(since = "13.0.0", forRemoval = true)
648     private static sealed class Item<T extends DataObject> extends AbstractPathArgument<T> {
649         @java.io.Serial
650         private static final long serialVersionUID = 1L;
651
652         Item(final Class<T> type) {
653             super(type);
654         }
655
656         @Override
657         @SuppressWarnings({ "rawtypes", "unchecked" })
658         final DataObjectStep<?> toStep() {
659             return DataObjectStep.of((Class) caseType(), type());
660         }
661
662         @Override
663         public String toString() {
664             return type().getName();
665         }
666     }
667
668     /**
669      * An IdentifiableItem represents a object that is usually present in a collection and can be identified uniquely
670      * by a key. In YANG terms this would probably represent an item in a list.
671      *
672      * @param <I> An object that is identifiable by an identifier
673      * @param <T> The identifier of the object
674      */
675     @Deprecated(since = "13.0.0", forRemoval = true)
676     private static sealed class IdentifiableItem<I extends KeyAware<T> & DataObject, T extends Key<I>>
677             extends AbstractPathArgument<I> {
678         @java.io.Serial
679         private static final long serialVersionUID = 1L;
680
681         private final @NonNull T key;
682
683         IdentifiableItem(final Class<I> type, final T key) {
684             super(type);
685             this.key = requireNonNull(key, "Key may not be null.");
686         }
687
688         /**
689          * Return the data object type backing this PathArgument.
690          *
691          * @return Data object type.
692          */
693         @Override
694         final @NonNull T key() {
695             return key;
696         }
697
698         @Override
699         final KeyStep<?, ?> toStep() {
700             return new KeyStep<>(type(), caseType(), key);
701         }
702
703         @Override
704         public String toString() {
705             return type().getName() + "[key=" + key + "]";
706         }
707     }
708
709     @Deprecated(since = "13.0.0", forRemoval = true)
710     private static final class CaseItem<C extends ChoiceIn<?> & DataObject, T extends ChildOf<? super C>>
711             extends Item<T> {
712         @java.io.Serial
713         private static final long serialVersionUID = 1L;
714
715         private final Class<C> caseType;
716
717         CaseItem(final Class<C> caseType, final Class<T> type) {
718             super(type);
719             this.caseType = requireNonNull(caseType);
720         }
721
722         @Override
723         Class<C> caseType() {
724             return caseType;
725         }
726     }
727
728     @Deprecated(since = "13.0.0", forRemoval = true)
729     private static final class CaseIdentifiableItem<C extends ChoiceIn<?> & DataObject,
730             T extends ChildOf<? super C> & KeyAware<K>, K extends Key<T>> extends IdentifiableItem<T, K> {
731         @java.io.Serial
732         private static final long serialVersionUID = 1L;
733
734         private final Class<C> caseType;
735
736         CaseIdentifiableItem(final Class<C> caseType, final Class<T> type, final K key) {
737             super(type, key);
738             this.caseType = requireNonNull(caseType);
739         }
740
741         @Override
742         Class<C> caseType() {
743             return caseType;
744         }
745     }
746
747     /**
748      * A builder of {@link InstanceIdentifier} objects.
749      *
750      * @param <T> Instance identifier target type
751      */
752     public abstract static sealed class Builder<T extends DataObject> extends AbstractDataObjectReferenceBuilder<T> {
753         Builder(final Builder<?> prev, final DataObjectStep<?> item) {
754             super(prev, item);
755         }
756
757         Builder(final InstanceIdentifier<T> identifier) {
758             super(identifier);
759         }
760
761         Builder(final DataObjectStep<?> item, final boolean wildcard) {
762             super(item, wildcard);
763         }
764
765         @Override
766         public final <N extends DataObject & Augmentation<? super T>> Builder<N> augmentation(
767                 final Class<N> augmentation) {
768             return append(new NodeStep<>(augmentation));
769         }
770
771         @Override
772         public final <N extends ChildOf<? super T>> Builder<N> child(final Class<N> container) {
773             return append(DataObjectStep.of(container));
774         }
775
776         @Override
777         public final <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>> Builder<N> child(
778                 final Class<C> caze, final Class<N> container) {
779             return append(DataObjectStep.of(caze, container));
780         }
781
782         @Override
783         public final <N extends KeyAware<K> & ChildOf<? super T>, K extends Key<N>> KeyedBuilder<N, K> child(
784                 final Class<@NonNull N> listItem, final K listKey) {
785             return append(new KeyStep<>(listItem, listKey));
786         }
787
788         @Override
789         public final <C extends ChoiceIn<? super T> & DataObject, K extends Key<N>,
790                 N extends KeyAware<K> & ChildOf<? super C>> KeyedBuilder<N, K> child(final Class<C> caze,
791                     final Class<N> listItem, final K listKey) {
792             return append(new KeyStep<>(listItem, requireNonNull(caze), listKey));
793         }
794
795         @Override
796         public abstract @NonNull InstanceIdentifier<T> build();
797
798         @Override
799         protected abstract <X extends DataObject> @NonNull RegularBuilder<X> append(DataObjectStep<X> step);
800
801         @Override
802         protected abstract <X extends DataObject & KeyAware<Y>, Y extends Key<X>> @NonNull KeyedBuilder<X, Y> append(
803             KeyStep<Y, X> step);
804     }
805
806     public static final class KeyedBuilder<T extends DataObject & KeyAware<K>, K extends Key<T>>
807             extends Builder<T> implements DataObjectReference.Builder.WithKey<T, K> {
808         KeyedBuilder(final KeyStep<K, T> firstStep) {
809             super(firstStep, false);
810         }
811
812         KeyedBuilder(final KeyedInstanceIdentifier<T, K> identifier) {
813             super(identifier);
814         }
815
816         private KeyedBuilder(final RegularBuilder<?> prev, final KeyStep<K, T> lastStep) {
817             super(prev, lastStep);
818         }
819
820         /**
821          * Build the instance identifier.
822          *
823          * @return Resulting {@link KeyedInstanceIdentifier}.
824          */
825         @Override
826         public @NonNull KeyedInstanceIdentifier<T, K> build() {
827             return new KeyedInstanceIdentifier<>(buildSteps(), wildcard());
828         }
829
830         @Override
831         protected <X extends DataObject> @NonNull RegularBuilder<X> append(final DataObjectStep<X> step) {
832             return new RegularBuilder<>(this, step);
833         }
834
835         @Override
836         @SuppressWarnings("unchecked")
837         protected <X extends DataObject & KeyAware<Y>, Y extends Key<X>> KeyedBuilder<X, Y> append(
838                 final KeyStep<Y, X> step) {
839             appendItem(step);
840             return (KeyedBuilder<X, Y>) this;
841         }
842     }
843
844     private static final class RegularBuilder<T extends DataObject> extends Builder<T> {
845         RegularBuilder(final DataObjectStep<T> item) {
846             super(item, !(item instanceof ExactDataObjectStep));
847         }
848
849         RegularBuilder(final InstanceIdentifier<T> identifier) {
850             super(identifier);
851         }
852
853         private RegularBuilder(final KeyedBuilder<?, ?> prev, final DataObjectStep<T> item) {
854             super(prev, item);
855         }
856
857         @Override
858         public InstanceIdentifier<T> build() {
859             return new InstanceIdentifier<>(buildSteps(), wildcard());
860         }
861
862         @Override
863         @SuppressWarnings("unchecked")
864         protected <X extends DataObject> RegularBuilder<X> append(final DataObjectStep<X> step) {
865             appendItem(step);
866             return (RegularBuilder<X>) this;
867         }
868
869         @Override
870         protected <X extends DataObject & KeyAware<Y>, Y extends Key<X>> KeyedBuilder<X, Y> append(
871                 final KeyStep<Y, X> item) {
872             return new KeyedBuilder<>(this, item);
873         }
874     }
875 }