2 * Copyright (c) 2014 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.data.api;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
13 import com.google.common.annotations.Beta;
14 import com.google.common.base.VerifyException;
15 import com.google.common.cache.CacheBuilder;
16 import com.google.common.cache.CacheLoader;
17 import com.google.common.cache.LoadingCache;
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMap;
20 import com.google.common.collect.Iterables;
21 import java.io.Serializable;
22 import java.lang.invoke.MethodHandles;
23 import java.lang.invoke.VarHandle;
24 import java.lang.reflect.Array;
25 import java.util.AbstractMap.SimpleImmutableEntry;
26 import java.util.Arrays;
27 import java.util.Collection;
28 import java.util.Deque;
29 import java.util.Iterator;
30 import java.util.List;
32 import java.util.Map.Entry;
33 import java.util.Objects;
34 import java.util.Optional;
36 import java.util.function.Function;
37 import org.eclipse.jdt.annotation.NonNull;
38 import org.eclipse.jdt.annotation.Nullable;
39 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
40 import org.opendaylight.yangtools.concepts.Immutable;
41 import org.opendaylight.yangtools.concepts.Mutable;
42 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
43 import org.opendaylight.yangtools.util.SingletonSet;
44 import org.opendaylight.yangtools.yang.common.QName;
45 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
48 * Unique identifier of a particular node instance in the data tree.
51 * Java representation of YANG Built-in type {@code instance-identifier}, which conceptually is XPath expression
52 * minimized to uniquely identify element in data tree which conforms to constraints maintained by YANG Model,
53 * effectively this makes Instance Identifier a path to element in data tree.
56 * Constraints put in YANG specification on instance-identifier allowed it to be effectively represented in Java and its
57 * evaluation does not require a full-blown XPath processor.
59 * <h2>Path Arguments</h2>
60 * Path to the node represented in instance identifier consists of {@link PathArgument} which carries necessary
61 * information to uniquely identify node on particular level in the subtree.
64 * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality {@code 0..1} in particular subtree in data
66 * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item), which has cardinality {@code 0..n}</li>
67 * <li>{@link NodeWithValue} - Identifier of instance {@code leaf} node or {@code leaf-list} node</li>
70 * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
72 public abstract sealed class YangInstanceIdentifier implements HierarchicalIdentifier<YangInstanceIdentifier>
73 permits FixedYangInstanceIdentifier, StackedYangInstanceIdentifier {
75 private static final long serialVersionUID = 4L;
76 private static final VarHandle TO_STRING_CACHE;
77 private static final VarHandle HASH;
80 final var lookup = MethodHandles.lookup();
82 HASH = lookup.findVarHandle(YangInstanceIdentifier.class, "hash", int.class);
83 TO_STRING_CACHE = lookup.findVarHandle(YangInstanceIdentifier.class, "toStringCache", String.class);
84 } catch (NoSuchFieldException | IllegalAccessException e) {
85 throw new ExceptionInInitializerError(e);
89 @SuppressWarnings("unused")
91 @SuppressWarnings("unused")
92 private transient String toStringCache = null;
94 YangInstanceIdentifier() {
95 // Package-private to prevent outside subclassing
99 * Return An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG
102 * @return An empty YangInstanceIdentifier
104 public static @NonNull YangInstanceIdentifier empty() {
105 return FixedYangInstanceIdentifier.EMPTY_INSTANCE;
108 abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
110 abstract @Nullable List<PathArgument> tryPathArguments();
112 abstract @Nullable List<PathArgument> tryReversePathArguments();
115 * Check if this instance identifier has empty path arguments, e.g. it is
116 * empty and corresponds to {@link #empty()}.
118 * @return True if this instance identifier is empty, false otherwise.
120 public abstract boolean isEmpty();
123 * Return an optimized version of this identifier, useful when the identifier
124 * will be used very frequently.
126 * @return A optimized equivalent instance.
128 public abstract @NonNull YangInstanceIdentifier toOptimized();
131 * Return the conceptual parent {@link YangInstanceIdentifier}, which has
132 * one item less in {@link #getPathArguments()}.
134 * @return Parent {@link YangInstanceIdentifier}, or null if this object is {@link #empty()}.
136 public abstract @Nullable YangInstanceIdentifier getParent();
139 * Return the conceptual parent {@link YangInstanceIdentifier}, which has one item less in
140 * {@link #getPathArguments()}.
142 * @return Parent {@link YangInstanceIdentifier}
143 * @throws VerifyException if this object is {@link #empty()}.
145 public abstract @NonNull YangInstanceIdentifier coerceParent();
148 * Return the ancestor {@link YangInstanceIdentifier} with a particular depth, e.g. number of path arguments.
150 * @param depth Ancestor depth
151 * @return Ancestor {@link YangInstanceIdentifier}
152 * @throws IllegalArgumentException if the specified depth is negative or is greater than the depth of this object.
154 public abstract @NonNull YangInstanceIdentifier getAncestor(int depth);
157 * Returns an ordered iteration of path arguments.
159 * @return Immutable iteration of path arguments.
161 public abstract @NonNull List<PathArgument> getPathArguments();
164 * Returns an iterable of path arguments in reverse order. This is useful
165 * when walking up a tree organized this way.
167 * @return Immutable iterable of path arguments in reverse order.
169 public abstract @NonNull List<PathArgument> getReversePathArguments();
172 * Returns the last PathArgument. This is equivalent of iterating
173 * to the last element of the iterable returned by {@link #getPathArguments()}.
175 * @return The last past argument, or null if there are no PathArguments.
177 public abstract PathArgument getLastPathArgument();
179 public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
180 return Iterables.isEmpty(path) ? empty() : new FixedYangInstanceIdentifier(ImmutableList.copyOf(path));
183 public static @NonNull YangInstanceIdentifier create(final PathArgument pathArgument) {
184 return new FixedYangInstanceIdentifier(ImmutableList.of(pathArgument));
187 public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
188 // We are forcing a copy, since we cannot trust the user
189 return create(Arrays.asList(path));
193 * Create a {@link YangInstanceIdentifier} by taking a snapshot of provided path and iterating it backwards.
195 * @param pathTowardsRoot Path towards root
196 * @return A {@link YangInstanceIdentifier} instance
197 * @throws NullPointerException if {@code pathTowardsRoot} or any of its members is null
199 public static @NonNull YangInstanceIdentifier createReverse(final Deque<PathArgument> pathTowardsRoot) {
200 final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
201 pathTowardsRoot.size());
202 pathTowardsRoot.descendingIterator().forEachRemaining(builder::add);
203 return YangInstanceIdentifier.create(builder.build());
207 * Create a {@link YangInstanceIdentifier} by walking specified stack backwards and extracting path components
210 * @param stackTowardsRoot Stack towards root,
211 * @return A {@link YangInstanceIdentifier} instance
212 * @throws NullPointerException if {@code pathTowardsRoot} is null
214 public static <T> @NonNull YangInstanceIdentifier createReverse(final Deque<? extends T> stackTowardsRoot,
215 final Function<T, PathArgument> function) {
216 final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
217 stackTowardsRoot.size());
218 final Iterator<? extends T> it = stackTowardsRoot.descendingIterator();
219 while (it.hasNext()) {
220 builder.add(function.apply(it.next()));
222 return YangInstanceIdentifier.create(builder.build());
225 boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
226 return getPathArguments().equals(other.getPathArguments());
230 public final boolean equals(final Object obj) {
231 return this == obj || obj instanceof YangInstanceIdentifier other && pathArgumentsEqual(other);
235 * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments.
237 * @param name QName of {@link NodeIdentifier}
238 * @return Instance Identifier with additional path argument added to the end.
240 public final @NonNull YangInstanceIdentifier node(final QName name) {
241 return node(new NodeIdentifier(name));
245 * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments.
247 * @param arg Path argument which should be added to the end
248 * @return Instance Identifier with additional path argument added to the end.
250 public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
251 return new StackedYangInstanceIdentifier(this, arg);
255 * Get the relative path from an ancestor. This method attempts to perform
256 * the reverse of concatenating a base (ancestor) and a path.
259 * Ancestor against which the relative path should be calculated
260 * @return This object's relative path from parent, or Optional.absent() if
261 * the specified parent is not in fact an ancestor of this object.
263 public Optional<YangInstanceIdentifier> relativeTo(final YangInstanceIdentifier ancestor) {
264 if (this == ancestor) {
265 return Optional.of(empty());
267 if (ancestor.isEmpty()) {
268 return Optional.of(this);
271 final Iterator<PathArgument> lit = getPathArguments().iterator();
272 final Iterator<PathArgument> oit = ancestor.getPathArguments().iterator();
275 while (oit.hasNext()) {
276 // Ancestor is not really an ancestor
277 if (!lit.hasNext() || !lit.next().equals(oit.next())) {
278 return Optional.empty();
285 return Optional.of(this);
287 if (!lit.hasNext()) {
288 return Optional.of(empty());
291 return Optional.of(createRelativeIdentifier(common));
295 public final boolean contains(final YangInstanceIdentifier other) {
300 checkArgument(other != null, "other should not be null");
301 final Iterator<PathArgument> lit = getPathArguments().iterator();
302 final Iterator<PathArgument> oit = other.getPathArguments().iterator();
304 while (lit.hasNext()) {
305 if (!oit.hasNext()) {
309 if (!lit.next().equals(oit.next())) {
318 public final String toString() {
320 * The toStringCache is safe, since the object contract requires
321 * immutability of the object and all objects referenced from this
323 * Used lists, maps are immutable. Path Arguments (elements) are also
324 * immutable, since the PathArgument contract requires immutability.
325 * The cache is thread-safe - if multiple computations occurs at the
326 * same time, cache will be overwritten with same result.
328 final String ret = (String) TO_STRING_CACHE.getAcquire(this);
329 return ret != null ? ret : loadToString();
332 private String loadToString() {
333 final StringBuilder builder = new StringBuilder("/");
334 PathArgument prev = null;
335 for (PathArgument argument : getPathArguments()) {
339 builder.append(argument.toRelativeString(prev));
343 final String ret = builder.toString();
344 final String witness = (String) TO_STRING_CACHE.compareAndExchangeRelease(this, null, ret);
345 return witness == null ? ret : witness;
349 public final int hashCode() {
351 * The caching is safe, since the object contract requires
352 * immutability of the object and all objects referenced from this
354 * Used lists, maps are immutable. Path Arguments (elements) are also
355 * immutable, since the PathArgument contract requires immutability.
357 final int local = (int) HASH.getAcquire(this);
358 return local != 0 ? local : loadHashCode();
361 private static int hashCode(final Object value) {
366 if (byte[].class.equals(value.getClass())) {
367 return Arrays.hashCode((byte[]) value);
370 if (value.getClass().isArray()) {
372 int length = Array.getLength(value);
373 for (int i = 0; i < length; i++) {
374 hash += Objects.hashCode(Array.get(value, i));
380 return Objects.hashCode(value);
383 private int loadHashCode() {
384 final int computed = computeHashCode();
385 HASH.setRelease(this, computed);
389 abstract int computeHashCode();
392 final Object writeReplace() {
393 return new YIDv1(this);
396 // Static factories & helpers
399 * Returns a new InstanceIdentifier with only one path argument of type {@link NodeIdentifier} with supplied
402 * @param name QName of first node identifier
403 * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
405 public static @NonNull YangInstanceIdentifier of(final QName name) {
406 return create(new NodeIdentifier(name));
410 * Returns new builder for InstanceIdentifier with empty path arguments.
412 * @return new builder for InstanceIdentifier with empty path arguments.
414 public static @NonNull InstanceIdentifierBuilder builder() {
415 return new YangInstanceIdentifierBuilder();
419 * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
421 * @param origin InstanceIdentifier from which path arguments are copied.
422 * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
424 public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
425 return new YangInstanceIdentifierBuilder(origin.getPathArguments());
429 * Path argument / component of InstanceIdentifier.
430 * Path argument uniquely identifies node in data tree on particular
434 * This interface itself is used as common parent for actual
435 * path arguments types and should not be implemented by user code.
438 * Path arguments SHOULD contain only minimum of information
439 * required to uniquely identify node on particular subtree level.
442 * For actual path arguments types see:
444 * <li>{@link NodeIdentifier} - Identifier of container or leaf
445 * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
446 * <li>{@link NodeWithValue} - Identifier of leaf-list entry
449 public abstract static sealed class PathArgument implements Comparable<PathArgument>, Immutable, Serializable {
451 private static final long serialVersionUID = -4546547994250849340L;
453 private final @NonNull QName nodeType;
454 private transient volatile int hashValue;
456 protected PathArgument(final QName nodeType) {
457 this.nodeType = requireNonNull(nodeType);
461 * Returns unique QName of data node as defined in YANG Schema, if available.
465 public final @NonNull QName getNodeType() {
470 * Return the string representation of this object for use in context
471 * provided by a previous object. This method can be implemented in
472 * terms of {@link #toString()}, but implementations are encourage to
473 * reuse any context already emitted by the previous object.
475 * @param previous Previous path argument
476 * @return String representation
478 public @NonNull String toRelativeString(final PathArgument previous) {
479 if (previous != null && nodeType.getModule().equals(previous.nodeType.getModule())) {
480 return nodeType.getLocalName();
482 return nodeType.toString();
486 @SuppressWarnings("checkstyle:parameterName")
487 public int compareTo(final PathArgument o) {
488 return nodeType.compareTo(o.nodeType);
491 protected int hashCodeImpl() {
492 return nodeType.hashCode();
496 public final int hashCode() {
498 return (local = hashValue) != 0 ? local : (hashValue = hashCodeImpl());
502 public boolean equals(final Object obj) {
506 if (obj == null || this.getClass() != obj.getClass()) {
510 return nodeType.equals(((PathArgument) obj).nodeType);
514 public String toString() {
515 return nodeType.toString();
519 abstract Object writeReplace();
523 * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
524 * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
526 public static final class NodeIdentifier extends PathArgument {
528 private static final long serialVersionUID = -2255888212390871347L;
529 private static final LoadingCache<QName, NodeIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
530 .build(new CacheLoader<QName, NodeIdentifier>() {
532 public NodeIdentifier load(final QName key) {
533 return new NodeIdentifier(key);
537 public NodeIdentifier(final QName node) {
542 * Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
543 * instance cache, resulting in object reuse for equal inputs.
545 * @param node Node's QName
546 * @return A {@link NodeIdentifier}
548 public static @NonNull NodeIdentifier create(final QName node) {
549 return CACHE.getUnchecked(node);
553 Object writeReplace() {
554 return new NIv1(this);
559 * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
562 public abstract static sealed class NodeIdentifierWithPredicates extends PathArgument {
564 public static final class Singleton extends NodeIdentifierWithPredicates {
566 private static final long serialVersionUID = 1L;
568 private final @NonNull QName key;
569 private final @NonNull Object value;
571 Singleton(final QName node, final QName key, final Object value) {
573 this.key = requireNonNull(key);
574 this.value = requireNonNull(value);
578 public SingletonSet<Entry<QName, Object>> entrySet() {
579 return SingletonSet.of(singleEntry());
583 public SingletonSet<QName> keySet() {
584 return SingletonSet.of(key);
588 public boolean containsKey(final QName qname) {
589 return key.equals(requireNonNull(qname));
593 public SingletonSet<Object> values() {
594 return SingletonSet.of(value);
603 public ImmutableMap<QName, Object> asMap() {
604 return ImmutableMap.of(key, value);
608 * Return the single entry contained in this object. This is equivalent to
609 * {@code entrySet().iterator().next()}.
611 * @return A single entry.
613 public @NonNull Entry<QName, Object> singleEntry() {
614 return new SimpleImmutableEntry<>(key, value);
618 boolean equalMapping(final NodeIdentifierWithPredicates other) {
619 final Singleton single = (Singleton) other;
620 return key.equals(single.key) && Objects.deepEquals(value, single.value);
624 Object keyValue(final QName qname) {
625 return key.equals(qname) ? value : null;
629 private static final class Regular extends NodeIdentifierWithPredicates {
631 private static final long serialVersionUID = 1L;
633 private final @NonNull Map<QName, Object> keyValues;
635 Regular(final QName node, final Map<QName, Object> keyValues) {
637 this.keyValues = requireNonNull(keyValues);
641 public Set<Entry<QName, Object>> entrySet() {
642 return keyValues.entrySet();
646 public Set<QName> keySet() {
647 return keyValues.keySet();
651 public boolean containsKey(final QName qname) {
652 return keyValues.containsKey(requireNonNull(qname));
656 public Collection<Object> values() {
657 return keyValues.values();
662 return keyValues.size();
666 public Map<QName, Object> asMap() {
671 Object keyValue(final QName qname) {
672 return keyValues.get(qname);
676 boolean equalMapping(final NodeIdentifierWithPredicates other) {
677 final Map<QName, Object> otherKeyValues = ((Regular) other).keyValues;
678 // TODO: benchmark to see if just calling equals() on the two maps is not faster
679 if (keyValues == otherKeyValues) {
682 if (keyValues.size() != otherKeyValues.size()) {
686 for (Entry<QName, Object> entry : entrySet()) {
687 final Object otherValue = otherKeyValues.get(entry.getKey());
688 if (otherValue == null || !Objects.deepEquals(entry.getValue(), otherValue)) {
698 private static final long serialVersionUID = -4787195606494761540L;
700 NodeIdentifierWithPredicates(final QName node) {
704 public static @NonNull NodeIdentifierWithPredicates of(final QName node) {
705 return new Regular(node, ImmutableMap.of());
708 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) {
709 return new Singleton(node, key, value);
712 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Entry<QName, Object> entry) {
713 return of(node, entry.getKey(), entry.getValue());
716 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map<QName, Object> keyValues) {
717 return keyValues.size() == 1 ? of(keyValues, node)
718 // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
719 : new Regular(node, ImmutableOffsetMap.unorderedCopyOf(keyValues));
722 public static @NonNull NodeIdentifierWithPredicates of(final QName node,
723 final ImmutableOffsetMap<QName, Object> keyValues) {
724 return keyValues.size() == 1 ? of(keyValues, node) : new Regular(node, keyValues);
727 private static @NonNull NodeIdentifierWithPredicates of(final Map<QName, Object> keyValues, final QName node) {
728 return of(node, keyValues.entrySet().iterator().next());
732 * Return the set of predicates keys and values. Keys are guaranteeed to be unique.
734 * @return Predicate set.
736 public abstract @NonNull Set<Entry<QName, Object>> entrySet();
739 * Return the predicate key in the iteration order of {@link #entrySet()}.
741 * @return Predicate values.
743 public abstract @NonNull Set<QName> keySet();
746 * Determine whether a particular predicate key is present.
748 * @param key Predicate key
749 * @return True if the predicate is present, false otherwise
750 * @throws NullPointerException if {@code key} is null
752 public abstract boolean containsKey(QName key);
755 * Return the predicate values in the iteration order of {@link #entrySet()}.
757 * @return Predicate values.
759 public abstract @NonNull Collection<Object> values();
762 public final @Nullable Object getValue(final QName key) {
763 return keyValue(requireNonNull(key));
767 public final <T> @Nullable T getValue(final QName key, final Class<T> valueClass) {
768 return valueClass.cast(getValue(key));
772 * Return the number of predicates present.
774 * @return The number of predicates present.
776 public abstract int size();
779 * A Map-like view of this identifier's predicates. The view is expected to be stable and effectively-immutable.
781 * @return Map of predicates.
784 public abstract @NonNull Map<QName, Object> asMap();
787 protected final int hashCodeImpl() {
788 int result = 31 * super.hashCodeImpl();
789 for (Entry<QName, Object> entry : entrySet()) {
790 result += entry.getKey().hashCode() + YangInstanceIdentifier.hashCode(entry.getValue());
796 @SuppressWarnings("checkstyle:equalsHashCode")
797 public final boolean equals(final Object obj) {
798 return super.equals(obj) && equalMapping((NodeIdentifierWithPredicates) obj);
801 abstract boolean equalMapping(NodeIdentifierWithPredicates other);
803 abstract @Nullable Object keyValue(@NonNull QName qname);
806 public final String toString() {
807 return super.toString() + '[' + asMap() + ']';
811 public final String toRelativeString(final PathArgument previous) {
812 return super.toRelativeString(previous) + '[' + asMap() + ']';
816 final Object writeReplace() {
817 return new NIPv2(this);
822 * Simple path argument identifying a {@link LeafSetEntryNode} leaf
825 public static final class NodeWithValue<T> extends PathArgument {
827 private static final long serialVersionUID = -3637456085341738431L;
829 private final @NonNull T value;
831 public NodeWithValue(final QName node, final T value) {
833 this.value = requireNonNull(value);
836 public @NonNull T getValue() {
841 protected int hashCodeImpl() {
842 return 31 * super.hashCodeImpl() + YangInstanceIdentifier.hashCode(value);
846 @SuppressWarnings("checkstyle:equalsHashCode")
847 public boolean equals(final Object obj) {
848 if (!super.equals(obj)) {
851 final NodeWithValue<?> other = (NodeWithValue<?>) obj;
852 return Objects.deepEquals(value, other.value);
856 public String toString() {
857 return super.toString() + '[' + value + ']';
861 public String toRelativeString(final PathArgument previous) {
862 return super.toRelativeString(previous) + '[' + value + ']';
866 Object writeReplace() {
867 return new NIVv1(this);
872 * Fluent Builder of Instance Identifier instances.
874 public interface InstanceIdentifierBuilder extends Mutable {
876 * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
878 * @param arg A {@link PathArgument} to be added
879 * @return this builder
881 @NonNull InstanceIdentifierBuilder node(PathArgument arg);
884 * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
886 * @param nodeType QName of {@link NodeIdentifier} which will be added
887 * @return this builder
889 @NonNull InstanceIdentifierBuilder node(QName nodeType);
892 * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
893 * instance identifier.
895 * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
896 * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
897 * @return this builder
899 @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
902 * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
904 * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
905 * @param key QName of key which will be added
906 * @param value value of key which will be added
907 * @return this builder
909 @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
912 * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
914 * @param args {@link PathArgument}s to be added
915 * @return this builder
916 * @throws NullPointerException if any of the arguments is null
918 @NonNull InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
921 * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
923 * @param args {@link PathArgument}s to be added
924 * @return this builder
925 * @throws NullPointerException if any of the arguments is null
927 default @NonNull InstanceIdentifierBuilder append(final PathArgument... args) {
928 return append(Arrays.asList(args));
932 * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
934 * @return {@link YangInstanceIdentifier}
936 @NonNull YangInstanceIdentifier build();