2 * Copyright (c) 2013 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.binding;
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;
15 import com.google.common.annotations.Beta;
16 import com.google.common.base.MoreObjects;
17 import com.google.common.base.MoreObjects.ToStringHelper;
18 import com.google.common.base.VerifyException;
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.Iterables;
21 import java.io.IOException;
22 import java.io.NotSerializableException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.io.ObjectStreamException;
26 import java.io.Serializable;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Objects;
30 import org.eclipse.jdt.annotation.NonNull;
31 import org.eclipse.jdt.annotation.Nullable;
32 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
33 import org.opendaylight.yangtools.util.HashCodeBuilder;
36 * This instance identifier uniquely identifies a specific DataObject in the data tree modeled by YANG.
39 * For Example let's say you were trying to refer to a node in inventory which was modeled in YANG as follows,
41 * module opendaylight-inventory {
47 * ext:context-instance "node-context";
56 * You can create an instance identifier as follows to get to a node with id "openflow:1": {@code
57 * InstanceIdentifier.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build();
61 * This would be the same as using a path like so, "/nodes/node/openflow:1" to refer to the openflow:1 node
63 public sealed class InstanceIdentifier<T extends DataObject>
64 implements HierarchicalIdentifier<InstanceIdentifier<? extends DataObject>>
65 permits KeyedInstanceIdentifier {
67 private static final long serialVersionUID = 3L;
70 * Protected to differentiate internal and external access. Internal access is required never to modify
71 * the contents. References passed to outside entities have to be wrapped in an unmodifiable view.
73 final Iterable<DataObjectStep<?>> pathArguments;
75 private final @NonNull Class<T> targetType;
76 private final boolean wildcarded;
77 private final int hash;
79 InstanceIdentifier(final Class<T> type, final Iterable<DataObjectStep<?>> pathArguments, final boolean wildcarded,
81 this.pathArguments = requireNonNull(pathArguments);
82 targetType = requireNonNull(type);
83 this.wildcarded = wildcarded;
88 * Return the type of data which this InstanceIdentifier identifies.
92 public final @NonNull Class<T> getTargetType() {
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.
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
104 @SuppressWarnings("unchecked")
105 public final <N extends DataObject> @NonNull InstanceIdentifier<N> verifyTarget(final Class<@NonNull N> target) {
106 verify(target.equals(targetType), "Cannot adapt %s to %s", this, target);
107 return (InstanceIdentifier<N>) this;
111 * Return the path argument chain which makes up this instance identifier.
113 * @return Path argument chain. Immutable and does not contain nulls.
115 public final @NonNull Iterable<DataObjectStep<?>> getPathArguments() {
116 return Iterables.unmodifiableIterable(pathArguments);
120 * Check whether an instance identifier contains any wildcards. A wildcard is an path argument which has a null key.
122 * @return true if any of the path arguments has a null key.
124 public final boolean isWildcarded() {
129 public final int hashCode() {
134 public final boolean equals(final Object obj) {
141 if (getClass() != obj.getClass()) {
145 final var other = (InstanceIdentifier<?>) obj;
146 if (pathArguments == other.pathArguments) {
151 * We could now just go and compare the pathArguments, but that can be potentially expensive. Let's try to avoid
152 * that by checking various things that we have cached from pathArguments and trying to prove the identifiers
155 return hash == other.hash && wildcarded == other.wildcarded && targetType == other.targetType
157 // Everything checks out so far, so we have to do a full equals
158 && Iterables.elementsEqual(pathArguments, other.pathArguments);
161 boolean keyEquals(final InstanceIdentifier<?> other) {
166 public final String toString() {
167 return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
171 * Add class-specific toString attributes.
173 * @param toStringHelper ToStringHelper instance
174 * @return ToStringHelper instance which was passed in
176 protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
177 return toStringHelper.add("targetType", targetType).add("path", Iterables.toString(pathArguments));
181 * Return an instance identifier trimmed at the first occurrence of a specific component type.
184 * For example let's say an instance identifier was built like so,
186 * identifier = InstanceIdentifier.builder(Nodes.class).child(Node.class,
187 * new NodeKey(new NodeId("openflow:1")).build();
191 * And you wanted to obtain the Instance identifier which represented Nodes you would do it like so,
195 * identifier.firstIdentifierOf(Nodes.class)
198 * @param type component type
199 * @return trimmed instance identifier, or null if the component type
202 public final <I extends DataObject> @Nullable InstanceIdentifier<I> firstIdentifierOf(
203 final Class<@NonNull I> type) {
205 for (var step : pathArguments) {
206 if (type.equals(step.type())) {
207 @SuppressWarnings("unchecked")
208 final var ret = (InstanceIdentifier<I>) internalCreate(Iterables.limit(pathArguments, count));
219 * Return the key associated with the first component of specified type in
222 * @param listItem component type
223 * @return key associated with the component, or null if the component type
226 public final <N extends KeyAware<K> & DataObject, K extends Key<N>> @Nullable K firstKeyOf(
227 final Class<@NonNull N> listItem) {
228 for (var step : pathArguments) {
229 if (step instanceof KeyStep<?, ?> keyPredicate && listItem.equals(step.type())) {
230 @SuppressWarnings("unchecked")
231 final var ret = (K) keyPredicate.key();
239 * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all
240 * PathArguments to match exactly.
243 * The contains method checks if the other identifier is fully contained within the current identifier. It does this
244 * by looking at only the types of the path arguments and not by comparing the path arguments themselves.
247 * To illustrate here is an example which explains the working of this API. Let's say you have two instance
248 * identifiers as follows:
250 * this = /nodes/node/openflow:1
251 * other = /nodes/node/openflow:2
253 * then this.contains(other) will return false.
255 * @param other Potentially-container instance identifier
256 * @return True if the specified identifier is contained in this identifier.
259 public final boolean contains(final InstanceIdentifier<? extends DataObject> other) {
260 requireNonNull(other, "other should not be null");
262 final var oit = other.pathArguments.iterator();
263 for (var step : pathArguments) {
264 if (!oit.hasNext()) {
267 if (!step.equals(oit.next())) {
275 * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar
276 * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their
277 * non-wildcarded PathArgument counterpart.
279 * @param other Identifier which should be checked for inclusion.
280 * @return true if this identifier contains the other object
282 public final boolean containsWildcarded(final InstanceIdentifier<?> other) {
283 requireNonNull(other, "other should not be null");
285 final var otherSteps = other.pathArguments.iterator();
286 for (var step : pathArguments) {
287 if (!otherSteps.hasNext()) {
291 final var otherStep = otherSteps.next();
292 if (step instanceof ExactDataObjectStep) {
293 if (!step.equals(otherStep)) {
296 } else if (step instanceof KeylessStep<?> keyless) {
297 if (!keyless.matches(otherStep)) {
301 throw new IllegalStateException("Unhandled step " + step);
308 private <N extends DataObject> @NonNull InstanceIdentifier<N> childIdentifier(final DataObjectStep<N> arg) {
309 return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)),
310 HashCodeBuilder.nextHashCode(hash, arg), wildcarded);
314 * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
315 * {@code builder().child(container).build()}.
317 * @param container Container to append
318 * @param <N> Container type
319 * @return An InstanceIdentifier.
320 * @throws NullPointerException if {@code container} is null
322 public final <N extends ChildOf<? super T>> @NonNull InstanceIdentifier<N> child(
323 final Class<@NonNull N> container) {
324 return childIdentifier(createStep(container));
328 * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
329 * {@code builder().child(listItem, listKey).build()}.
331 * @param listItem List to append
332 * @param listKey List key
333 * @param <N> List type
334 * @param <K> Key type
335 * @return An InstanceIdentifier.
336 * @throws NullPointerException if any argument is null
338 @SuppressWarnings("unchecked")
339 public final <N extends KeyAware<K> & ChildOf<? super T>, K extends Key<N>>
340 @NonNull KeyedInstanceIdentifier<N, K> child(final Class<@NonNull N> listItem, final K listKey) {
341 return (KeyedInstanceIdentifier<N, K>) childIdentifier(new KeyStep<>(listItem, listKey));
345 * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
346 * {@code builder().child(caze, container).build()}.
348 * @param caze Choice case class
349 * @param container Container to append
350 * @param <C> Case type
351 * @param <N> Container type
352 * @return An InstanceIdentifier.
353 * @throws NullPointerException if any argument is null
355 // FIXME: add a proper caller
356 public final <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>>
357 @NonNull InstanceIdentifier<N> child(final Class<@NonNull C> caze, final Class<@NonNull N> container) {
358 return childIdentifier(createStep(caze, container));
362 * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
363 * {@code builder().child(caze, listItem, listKey).build()}.
365 * @param caze Choice case class
366 * @param listItem List to append
367 * @param listKey List key
368 * @param <C> Case type
369 * @param <N> List type
370 * @param <K> Key type
371 * @return An InstanceIdentifier.
372 * @throws NullPointerException if any argument is null
374 // FIXME: add a proper caller
375 @SuppressWarnings("unchecked")
376 public final <C extends ChoiceIn<? super T> & DataObject, K extends Key<N>,
377 N extends KeyAware<K> & ChildOf<? super C>> @NonNull KeyedInstanceIdentifier<N, K> child(
378 final Class<@NonNull C> caze, final Class<@NonNull N> listItem, final K listKey) {
379 return (KeyedInstanceIdentifier<N, K>) childIdentifier(new KeyStep<>(listItem, requireNonNull(caze), listKey));
383 * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to
384 * {@code builder().augmentation(container).build()}.
386 * @param container Container to append
387 * @param <N> Container type
388 * @return An InstanceIdentifier.
389 * @throws NullPointerException if {@code container} is null
391 public final <N extends DataObject & Augmentation<? super T>> @NonNull InstanceIdentifier<N> augmentation(
392 final Class<@NonNull N> container) {
393 return childIdentifier(new NodeStep<>(container));
397 Object writeReplace() throws ObjectStreamException {
398 return new IIv4<>(this);
402 private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException {
407 private void readObjectNoData() throws ObjectStreamException {
412 private void writeObject(final ObjectOutputStream stream) throws IOException {
416 private void throwNSE() throws NotSerializableException {
417 throw new NotSerializableException(getClass().getName());
421 * Create a builder rooted at this key.
423 * @return A builder instance
425 // FIXME: rename this method to 'toBuilder()'
426 public @NonNull Builder<T> builder() {
427 return new RegularBuilder<>(this);
431 * Create a {@link Builder} for a specific type of InstanceIdentifier as specified by container.
433 * @param container Base container
434 * @param <T> Type of the container
435 * @return A new {@link Builder}
436 * @throws NullPointerException if {@code container} is null
438 public static <T extends ChildOf<? extends DataRoot>> @NonNull Builder<T> builder(
439 final Class<T> container) {
440 return new RegularBuilder<>(createStep(container));
444 * Create a {@link Builder} for a specific type of InstanceIdentifier as specified by container in
445 * a {@code grouping} used in the {@code case} statement.
447 * @param caze Choice case class
448 * @param container Base container
449 * @param <C> Case type
450 * @param <T> Type of the container
451 * @return A new {@link Builder}
452 * @throws NullPointerException if any argument is null
454 public static <C extends ChoiceIn<? extends DataRoot> & DataObject, T extends ChildOf<? super C>>
455 @NonNull Builder<T> builder(final Class<C> caze, final Class<T> container) {
456 return new RegularBuilder<>(createStep(caze, container));
460 * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link IdentifiableItem}.
462 * @param listItem list item class
463 * @param listKey key value
464 * @param <N> List type
465 * @param <K> List key
466 * @return A new {@link Builder}
467 * @throws NullPointerException if any argument is null
469 public static <N extends KeyAware<K> & ChildOf<? extends DataRoot>,
470 K extends Key<N>> @NonNull KeyedBuilder<N, K> builder(final Class<N> listItem,
472 return new KeyedBuilder<>(new KeyStep<>(listItem, listKey));
476 * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link IdentifiableItem}
477 * in a {@code grouping} used in the {@code case} statement.
479 * @param caze Choice case class
480 * @param listItem list item class
481 * @param listKey key value
482 * @param <C> Case type
483 * @param <N> List type
484 * @param <K> List key
485 * @return A new {@link Builder}
486 * @throws NullPointerException if any argument is null
488 public static <C extends ChoiceIn<? extends DataRoot> & DataObject,
489 N extends KeyAware<K> & ChildOf<? super C>, K extends Key<N>>
490 @NonNull KeyedBuilder<N, K> builder(final Class<C> caze, final Class<N> listItem,
492 return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey));
495 public static <R extends DataRoot & DataObject, T extends ChildOf<? super R>>
496 @NonNull Builder<T> builderOfInherited(final Class<R> root, final Class<T> container) {
497 // FIXME: we are losing root identity, hence namespaces may not work correctly
498 return new RegularBuilder<>(createStep(container));
501 public static <R extends DataRoot & DataObject, C extends ChoiceIn<? super R> & DataObject,
502 T extends ChildOf<? super C>>
503 @NonNull Builder<T> builderOfInherited(final Class<R> root,
504 final Class<C> caze, final Class<T> container) {
505 // FIXME: we are losing root identity, hence namespaces may not work correctly
506 return new RegularBuilder<>(createStep(caze, container));
509 public static <R extends DataRoot & DataObject, N extends KeyAware<K> & ChildOf<? super R>,
511 @NonNull KeyedBuilder<N, K> builderOfInherited(final Class<R> root,
512 final Class<N> listItem, final K listKey) {
513 // FIXME: we are losing root identity, hence namespaces may not work correctly
514 return new KeyedBuilder<>(new KeyStep<>(listItem, listKey));
517 public static <R extends DataRoot & DataObject, C extends ChoiceIn<? super R> & DataObject,
518 N extends KeyAware<K> & ChildOf<? super C>, K extends Key<N>>
519 @NonNull KeyedBuilder<N, K> builderOfInherited(final Class<R> root,
520 final Class<C> caze, final Class<N> listItem, final K listKey) {
521 // FIXME: we are losing root identity, hence namespaces may not work correctly
522 return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey));
526 @SuppressWarnings({ "rawtypes", "unchecked" })
527 public static <T extends DataObject, C extends ChoiceIn<?> & DataObject> @NonNull DataObjectStep<T> createStep(
528 final Class<C> caze, final Class<T> type) {
529 return KeyAware.class.isAssignableFrom(type) ? new KeylessStep(type, caze) : new NodeStep<>(type, caze);
533 public static <T extends DataObject> @NonNull DataObjectStep<T> createStep(final Class<T> type) {
534 return createStep(null, type);
538 * Create an instance identifier for a very specific object type. This method implements {@link #create(Iterable)}
539 * semantics, except it is used by internal callers, which have assured that the argument is an immutable Iterable.
541 * @param pathArguments The path to a specific node in the data tree
542 * @return InstanceIdentifier instance
543 * @throws IllegalArgumentException if pathArguments is empty or contains a null element.
544 * @throws NullPointerException if {@code pathArguments} is null
546 private static @NonNull InstanceIdentifier<?> internalCreate(final Iterable<DataObjectStep<?>> pathArguments) {
547 final var it = requireNonNull(pathArguments, "pathArguments may not be null").iterator();
548 checkArgument(it.hasNext(), "pathArguments may not be empty");
550 final var hashBuilder = new HashCodeBuilder<DataObjectStep<?>>();
551 boolean wildcard = false;
552 DataObjectStep<?> arg;
556 // Non-null is implied by our callers
557 final var type = verifyNotNull(arg).type();
558 checkArgument(ChildOf.class.isAssignableFrom(type) || Augmentation.class.isAssignableFrom(type),
559 "%s is not a valid path argument", type);
561 hashBuilder.addArgument(arg);
563 if (!(arg instanceof ExactDataObjectStep)) {
566 } while (it.hasNext());
568 return trustedCreate(arg, pathArguments, hashBuilder.build(), wildcard);
572 * Create an instance identifier for a sequence of {@link DataObjectStep} steps. The steps are required to be formed
573 * of classes extending either {@link ChildOf} or {@link Augmentation} contracts. This method does not check whether
574 * or not the sequence is structurally sound, for example that an {@link Augmentation} follows an
575 * {@link Augmentable} step. Furthermore the compile-time indicated generic type of the returned object does not
576 * necessarily match the contained state.
579 * Failure to observe precautions to validate the list's contents may yield an object which mey be rejected at
580 * run-time or lead to undefined behaviour.
582 * @param pathArguments The path to a specific node in the data tree
583 * @return InstanceIdentifier instance
584 * @throws NullPointerException if {@code pathArguments} is, or contains an item which is, {@code null}
585 * @throws IllegalArgumentException if {@code pathArguments} is empty or contains an item which does not represent
586 * a valid addressing step.
588 @SuppressWarnings("unchecked")
589 public static <T extends DataObject> @NonNull InstanceIdentifier<T> unsafeOf(
590 final List<? extends DataObjectStep<?>> pathArguments) {
591 return (InstanceIdentifier<T>) internalCreate(ImmutableList.copyOf(pathArguments));
595 * Create an instance identifier for a very specific object type.
600 * new InstanceIdentifier(Nodes.class)
602 * would create an InstanceIdentifier for an object of type Nodes
604 * @param type The type of the object which this instance identifier represents
605 * @return InstanceIdentifier instance
607 // FIXME: considering removing in favor of always going through a builder
608 @SuppressWarnings("unchecked")
609 public static <T extends ChildOf<? extends DataRoot>> @NonNull InstanceIdentifier<T> create(
610 final Class<@NonNull T> type) {
611 return (InstanceIdentifier<T>) internalCreate(ImmutableList.of(createStep(type)));
615 * Return the key associated with the last component of the specified identifier.
617 * @param id instance identifier
618 * @return key associated with the last component
619 * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
620 * @throws NullPointerException if id is null.
622 // FIXME: reconsider naming and design of this method
623 public static <N extends KeyAware<K> & DataObject, K extends Key<N>> K keyOf(
624 final InstanceIdentifier<N> id) {
626 checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
628 @SuppressWarnings("unchecked")
629 final K ret = ((KeyedInstanceIdentifier<N, K>)id).getKey();
633 @SuppressWarnings({ "unchecked", "rawtypes" })
634 static <N extends DataObject> @NonNull InstanceIdentifier<N> trustedCreate(final DataObjectStep<?> lastStep,
635 final Iterable<DataObjectStep<?>> pathArguments, final int hash, final boolean wildcarded) {
636 if (lastStep instanceof NodeStep) {
637 return new InstanceIdentifier(lastStep.type(), pathArguments, wildcarded, hash);
638 } else if (lastStep instanceof KeyStep<?, ?> predicate) {
639 return new KeyedInstanceIdentifier(predicate, pathArguments, wildcarded, hash);
640 } else if (lastStep instanceof KeylessStep) {
641 return new InstanceIdentifier(lastStep.type(), pathArguments, true, hash);
643 throw new IllegalStateException("Unhandled step " + lastStep);
647 @Deprecated(since = "13.0.0", forRemoval = true)
648 private abstract static sealed class AbstractPathArgument<T extends DataObject>
649 implements Comparable<AbstractPathArgument<?>>, Serializable {
651 private static final long serialVersionUID = 1L;
653 private final @NonNull Class<T> type;
655 AbstractPathArgument(final Class<T> type) {
656 this.type = requireNonNull(type, "Type may not be null.");
660 * Return the data object type backing this PathArgument.
662 * @return Data object type.
664 final @NonNull Class<T> type() {
669 * Return an optional enclosing case type. This is used only when {@link #type()} references a node defined
670 * in a {@code grouping} which is reference inside a {@code case} statement in order to safely reference the
673 * @return case class or {@code null}
675 Class<? extends DataObject> caseType() {
679 @Nullable Object key() {
684 public final int hashCode() {
685 return Objects.hash(type, caseType(), key());
689 public final boolean equals(final Object obj) {
690 return this == obj || obj instanceof AbstractPathArgument<?> other && type.equals(other.type)
691 && Objects.equals(key(), other.key()) && Objects.equals(caseType(), other.caseType());
695 public final int compareTo(final AbstractPathArgument<?> arg) {
696 final int cmp = compareClasses(type, arg.type());
700 final var caseType = caseType();
701 final var argCaseType = arg.caseType();
702 if (caseType == null) {
703 return argCaseType == null ? 1 : -1;
705 return argCaseType == null ? 1 : compareClasses(caseType, argCaseType);
708 private static int compareClasses(final Class<?> first, final Class<?> second) {
709 return first.getCanonicalName().compareTo(second.getCanonicalName());
713 final Object readResolve() throws ObjectStreamException {
717 abstract DataObjectStep<?> toStep();
721 * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
722 * a kind. In YANG terms this would probably represent a container.
724 * @param <T> Item type
726 @Deprecated(since = "13.0.0", forRemoval = true)
727 private static sealed class Item<T extends DataObject> extends AbstractPathArgument<T> {
729 private static final long serialVersionUID = 1L;
731 Item(final Class<T> type) {
736 @SuppressWarnings({ "rawtypes", "unchecked" })
737 final DataObjectStep<?> toStep() {
738 return createStep((Class) caseType(), type());
742 public String toString() {
743 return type().getName();
748 * An IdentifiableItem represents a object that is usually present in a collection and can be identified uniquely
749 * by a key. In YANG terms this would probably represent an item in a list.
751 * @param <I> An object that is identifiable by an identifier
752 * @param <T> The identifier of the object
754 @Deprecated(since = "13.0.0", forRemoval = true)
755 private static sealed class IdentifiableItem<I extends KeyAware<T> & DataObject, T extends Key<I>>
756 extends AbstractPathArgument<I> {
758 private static final long serialVersionUID = 1L;
760 private final @NonNull T key;
762 IdentifiableItem(final Class<I> type, final T key) {
764 this.key = requireNonNull(key, "Key may not be null.");
768 * Return the data object type backing this PathArgument.
770 * @return Data object type.
773 final @NonNull T key() {
778 final KeyStep<?, ?> toStep() {
779 return new KeyStep<>(type(), caseType(), key);
783 public String toString() {
784 return type().getName() + "[key=" + key + "]";
788 @Deprecated(since = "13.0.0", forRemoval = true)
789 private static final class CaseItem<C extends ChoiceIn<?> & DataObject, T extends ChildOf<? super C>>
792 private static final long serialVersionUID = 1L;
794 private final Class<C> caseType;
796 CaseItem(final Class<C> caseType, final Class<T> type) {
798 this.caseType = requireNonNull(caseType);
802 Class<C> caseType() {
807 @Deprecated(since = "13.0.0", forRemoval = true)
808 private static final class CaseIdentifiableItem<C extends ChoiceIn<?> & DataObject,
809 T extends ChildOf<? super C> & KeyAware<K>, K extends Key<T>> extends IdentifiableItem<T, K> {
811 private static final long serialVersionUID = 1L;
813 private final Class<C> caseType;
815 CaseIdentifiableItem(final Class<C> caseType, final Class<T> type, final K key) {
817 this.caseType = requireNonNull(caseType);
821 Class<C> caseType() {
827 * A builder of {@link InstanceIdentifier} objects.
829 * @param <T> Instance identifier target type
831 public abstract static sealed class Builder<T extends DataObject> {
832 private final ImmutableList.Builder<DataObjectStep<?>> pathBuilder;
833 private final HashCodeBuilder<DataObjectStep<?>> hashBuilder;
834 private final Iterable<? extends DataObjectStep<?>> basePath;
836 private boolean wildcard;
838 Builder(final Builder<?> prev, final DataObjectStep<?> item) {
839 pathBuilder = prev.pathBuilder;
840 hashBuilder = prev.hashBuilder;
841 basePath = prev.basePath;
842 wildcard = prev.wildcard;
846 Builder(final InstanceIdentifier<T> identifier) {
847 pathBuilder = ImmutableList.builder();
848 hashBuilder = new HashCodeBuilder<>(identifier.hashCode());
849 wildcard = identifier.isWildcarded();
850 basePath = identifier.pathArguments;
853 Builder(final DataObjectStep<?> item, final boolean wildcard) {
854 pathBuilder = ImmutableList.builder();
855 hashBuilder = new HashCodeBuilder<>();
857 hashBuilder.addArgument(item);
858 pathBuilder.add(item);
859 this.wildcard = wildcard;
862 final boolean wildcard() {
867 * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by
870 * @param container augmentation class
871 * @param <N> augmentation type
872 * @return this builder
873 * @throws NullPointerException if {@code container} is null
875 public final <N extends DataObject & Augmentation<? super T>> Builder<N> augmentation(
876 final Class<N> container) {
877 return append(new NodeStep<>(container));
881 * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This
882 * method should be used when you want to build an instance identifier by appending top-level elements, for
885 * InstanceIdentifier.builder().child(Nodes.class).build();
889 * NOTE :- The above example is only for illustration purposes InstanceIdentifier.builder() has been deprecated
890 * and should not be used. Use InstanceIdentifier.builder(Nodes.class) instead
892 * @param container Container to append
893 * @param <N> Container type
894 * @return this builder
895 * @throws NullPointerException if {@code container} is null
897 public final <N extends ChildOf<? super T>> Builder<N> child(final Class<N> container) {
898 return append(createStep(container));
902 * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This
903 * method should be used when you want to build an instance identifier by appending a container node to the
904 * identifier and the {@code container} is defined in a {@code grouping} used in a {@code case} statement.
906 * @param caze Choice case class
907 * @param container Container to append
908 * @param <C> Case type
909 * @param <N> Container type
910 * @return this builder
911 * @throws NullPointerException if {@code container} is null
913 public final <C extends ChoiceIn<? super T> & DataObject, N extends ChildOf<? super C>> Builder<N> child(
914 final Class<C> caze, final Class<N> container) {
915 return append(createStep(caze, container));
919 * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This
920 * method should be used when you want to build an instance identifier by appending a specific list element to
923 * @param listItem List to append
924 * @param listKey List key
925 * @param <N> List type
926 * @param <K> Key type
927 * @return this builder
928 * @throws NullPointerException if any argument is null
930 public final <N extends KeyAware<K> & ChildOf<? super T>, K extends Key<N>> KeyedBuilder<N, K> child(
931 final Class<@NonNull N> listItem, final K listKey) {
932 return append(new KeyStep<>(listItem, listKey));
936 * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This
937 * method should be used when you want to build an instance identifier by appending a specific list element to
938 * the identifier and the {@code list} is defined in a {@code grouping} used in a {@code case} statement.
940 * @param caze Choice case class
941 * @param listItem List to append
942 * @param listKey List key
943 * @param <C> Case type
944 * @param <N> List type
945 * @param <K> Key type
946 * @return this builder
947 * @throws NullPointerException if any argument is null
949 public final <C extends ChoiceIn<? super T> & DataObject, K extends Key<N>,
950 N extends KeyAware<K> & ChildOf<? super C>> KeyedBuilder<N, K> child(final Class<C> caze,
951 final Class<N> listItem, final K listKey) {
952 return append(new KeyStep<>(listItem, requireNonNull(caze), listKey));
956 * Build the instance identifier.
958 * @return Resulting {@link InstanceIdentifier}.
960 public abstract @NonNull InstanceIdentifier<T> build();
963 public final int hashCode() {
964 return hashBuilder.build();
968 public final boolean equals(final Object obj) {
969 return this == obj || obj instanceof Builder<?> other
970 && wildcard == other.wildcard && hashCode() == other.hashCode()
971 && Iterables.elementsEqual(pathArguments(), other.pathArguments());
974 final Iterable<DataObjectStep<?>> pathArguments() {
975 final var args = pathBuilder.build();
976 return basePath == null ? args : Iterables.concat(basePath, args);
979 final void appendItem(final DataObjectStep<?> item) {
980 hashBuilder.addArgument(item);
981 pathBuilder.add(item);
982 if (!(item instanceof ExactDataObjectStep)) {
987 abstract <X extends DataObject> @NonNull RegularBuilder<X> append(DataObjectStep<X> step);
989 abstract <X extends DataObject & KeyAware<Y>, Y extends Key<X>> @NonNull KeyedBuilder<X, Y> append(
993 public static final class KeyedBuilder<T extends DataObject & KeyAware<K>, K extends Key<T>>
995 private @NonNull KeyStep<K, T> lastStep;
997 KeyedBuilder(final KeyStep<K, T> firstStep) {
998 super(firstStep, false);
999 lastStep = requireNonNull(firstStep);
1002 KeyedBuilder(final KeyedInstanceIdentifier<T, K> identifier) {
1004 lastStep = identifier.lastStep();
1007 private KeyedBuilder(final RegularBuilder<?> prev, final KeyStep<K, T> lastStep) {
1008 super(prev, lastStep);
1009 this.lastStep = requireNonNull(lastStep);
1013 * Build the instance identifier.
1015 * @return Resulting {@link KeyedInstanceIdentifier}.
1018 public @NonNull KeyedInstanceIdentifier<T, K> build() {
1019 return new KeyedInstanceIdentifier<>(lastStep, pathArguments(), wildcard(), hashCode());
1023 <X extends DataObject> @NonNull RegularBuilder<X> append(final DataObjectStep<X> step) {
1024 return new RegularBuilder<>(this, step);
1028 @SuppressWarnings("unchecked")
1029 <X extends DataObject & KeyAware<Y>, Y extends Key<X>> KeyedBuilder<X, Y> append(final KeyStep<Y, X> step) {
1031 lastStep = (KeyStep<K, T>) requireNonNull(step);
1032 return (KeyedBuilder<X, Y>) this;
1036 private static final class RegularBuilder<T extends DataObject> extends Builder<T> {
1037 private @NonNull Class<T> type;
1039 RegularBuilder(final DataObjectStep<T> item) {
1040 super(item, !(item instanceof ExactDataObjectStep));
1044 RegularBuilder(final InstanceIdentifier<T> identifier) {
1046 type = identifier.getTargetType();
1049 private RegularBuilder(final KeyedBuilder<?, ?> prev, final DataObjectStep<T> item) {
1055 public InstanceIdentifier<T> build() {
1056 return new InstanceIdentifier<>(type, pathArguments(), wildcard(), hashCode());
1060 @SuppressWarnings({ "rawtypes", "unchecked" })
1061 <X extends DataObject> RegularBuilder<X> append(final DataObjectStep<X> step) {
1063 type = (Class) step.type();
1064 return (RegularBuilder<X>) this;
1068 <X extends DataObject & KeyAware<Y>, Y extends Key<X>> KeyedBuilder<X, Y> append(
1069 final KeyStep<Y, X> item) {
1070 return new KeyedBuilder<>(this, item);