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 java.lang.invoke.MethodHandles;
21 import java.lang.invoke.VarHandle;
22 import java.lang.reflect.Array;
23 import java.util.AbstractMap.SimpleImmutableEntry;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Deque;
27 import java.util.Iterator;
28 import java.util.List;
30 import java.util.Map.Entry;
31 import java.util.Objects;
32 import java.util.Optional;
34 import java.util.function.Function;
35 import org.eclipse.jdt.annotation.NonNull;
36 import org.eclipse.jdt.annotation.Nullable;
37 import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;
38 import org.opendaylight.yangtools.concepts.Identifier;
39 import org.opendaylight.yangtools.concepts.Mutable;
40 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
41 import org.opendaylight.yangtools.util.SingletonSet;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
46 * Unique identifier of a particular node instance in the data tree.
49 * Java representation of YANG Built-in type {@code instance-identifier}, which conceptually is XPath expression
50 * minimized to uniquely identify element in data tree which conforms to constraints maintained by YANG Model,
51 * effectively this makes Instance Identifier a path to element in data tree.
54 * Constraints put in YANG specification on instance-identifier allowed it to be effectively represented in Java and its
55 * evaluation does not require a full-blown XPath processor.
57 * <h2>Path Arguments</h2>
58 * Path to the node represented in instance identifier consists of {@link PathArgument} which carries necessary
59 * information to uniquely identify node on particular level in the subtree.
62 * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality {@code 0..1} in particular subtree in data
64 * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item), which has cardinality {@code 0..n}</li>
65 * <li>{@link NodeWithValue} - Identifier of instance {@code leaf} node or {@code leaf-list} node</li>
68 * @see <a href="http://www.rfc-editor.org/rfc/rfc6020#section-9.13">RFC6020</a>
70 public abstract sealed class YangInstanceIdentifier implements HierarchicalIdentifier<YangInstanceIdentifier>
71 permits FixedYangInstanceIdentifier, StackedYangInstanceIdentifier {
73 private static final long serialVersionUID = 4L;
74 private static final VarHandle TO_STRING_CACHE;
75 private static final VarHandle HASH;
78 final var lookup = MethodHandles.lookup();
80 HASH = lookup.findVarHandle(YangInstanceIdentifier.class, "hash", int.class);
81 TO_STRING_CACHE = lookup.findVarHandle(YangInstanceIdentifier.class, "toStringCache", String.class);
82 } catch (NoSuchFieldException | IllegalAccessException e) {
83 throw new ExceptionInInitializerError(e);
87 @SuppressWarnings("unused")
89 @SuppressWarnings("unused")
90 private transient String toStringCache = null;
92 YangInstanceIdentifier() {
93 // Package-private to prevent outside subclassing
97 * Return An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG
100 * @return An empty YangInstanceIdentifier
101 * @deprecated Use {@link #of()} instead.
103 @Deprecated(since = "11.0.0", forRemoval = true)
104 public static final @NonNull YangInstanceIdentifier empty() {
109 * Return an empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG
112 * @return An empty YangInstanceIdentifier
114 public static final @NonNull YangInstanceIdentifier of() {
115 return FixedYangInstanceIdentifier.EMPTY_INSTANCE;
119 * Returns a new InstanceIdentifier with only one path argument of type {@link PathArgument}.
121 * @param name QName of first node identifier
122 * @return A YangInstanceIdentifier
123 * @throws NullPointerException if {@code name} is {@code null}
125 public static final @NonNull YangInstanceIdentifier of(final PathArgument name) {
126 return new FixedYangInstanceIdentifier(ImmutableList.of(name));
130 * Returns a new InstanceIdentifier composed of supplied {@link PathArgument}s.
132 * @param path Path arguments
133 * @return A YangInstanceIdentifier
134 * @throws NullPointerException if {@code path} or any of its components is {@code null}
136 public static final @NonNull YangInstanceIdentifier of(final PathArgument... path) {
137 // We are forcing a copy, since we cannot trust the user
138 return of(ImmutableList.copyOf(path));
142 * Returns a new InstanceIdentifier composed of supplied {@link PathArgument}s.
144 * @param path Path arguments
145 * @return A YangInstanceIdentifier
146 * @throws NullPointerException if {@code path} is {@code null}
148 public static final @NonNull YangInstanceIdentifier of(final ImmutableList<PathArgument> path) {
149 return path.isEmpty() ? of() : new FixedYangInstanceIdentifier(path);
153 * Returns a new InstanceIdentifier composed of supplied {@link PathArgument}s.
155 * @param path Path arguments
156 * @return A YangInstanceIdentifier
157 * @throws NullPointerException if {@code path} or any of its components is {@code null}
159 public static final @NonNull YangInstanceIdentifier of(final Collection<? extends PathArgument> path) {
160 return path.isEmpty() ? of() : of(ImmutableList.copyOf(path));
164 * Returns a new InstanceIdentifier composed of supplied {@link PathArgument}s.
166 * @param path Path arguments
167 * @return A YangInstanceIdentifier
168 * @throws NullPointerException if {@code path} or any of its components is {@code null}
170 public static final @NonNull YangInstanceIdentifier of(final Iterable<? extends PathArgument> path) {
171 return of(ImmutableList.copyOf(path));
175 * Returns a new {@link YangInstanceIdentifier} with only one path argument of type {@link NodeIdentifier} with
176 * supplied {@link QName}. Note this is a convenience method aimed at test code. Production code should consider
177 * using {@link #of(PathArgument)} instead.
179 * @param name QName of first {@link NodeIdentifier}
180 * @return A YangInstanceIdentifier
181 * @throws NullPointerException if {@code name} is {@code null}
183 public static final @NonNull YangInstanceIdentifier of(final QName name) {
184 return of(new NodeIdentifier(name));
188 * Returns a new {@link YangInstanceIdentifier} with path arguments of type {@link NodeIdentifier} with
189 * supplied {@link QName}s. Note this is a convenience method aimed at test code. Production code should consider
190 * using {@link #of(PathArgument...)} instead.
192 * @param path QNames of {@link NodeIdentifier}s
193 * @return A YangInstanceIdentifier
194 * @throws NullPointerException if {@code path} or any of its components is {@code null}
196 public static final @NonNull YangInstanceIdentifier of(final QName... path) {
197 return of(Arrays.stream(path).map(NodeIdentifier::new).collect(ImmutableList.toImmutableList()));
201 * Create a YangInstanceIdentifier composed of a single {@link PathArgument}.
203 * @param pathArgument Path argument
204 * @return A {@link YangInstanceIdentifier}
205 * @throws NullPointerException if {@code pathArgument} is null
206 * @deprecated Use {@link #of(NodeIdentifier)} instead.
208 @Deprecated(since = "11.0.0", forRemoval = true)
209 public static @NonNull YangInstanceIdentifier create(final PathArgument pathArgument) {
210 return of(pathArgument);
214 * Create a YangInstanceIdentifier composed of specified {@link PathArgument}s.
216 * @param path Path arguments
217 * @return A {@link YangInstanceIdentifier}
218 * @throws NullPointerException if {@code path} or any of its components is {@code null}
219 * @deprecated Use {@link #of(PathArgument...)} instead.
221 @Deprecated(since = "11.0.0", forRemoval = true)
222 public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
227 * Create a YangInstanceIdentifier composed of specified {@link PathArgument}s.
229 * @param path Path arguments
230 * @return A {@link YangInstanceIdentifier}
231 * @throws NullPointerException if {@code path} or any of its components is {@code null}
232 * @deprecated Use {@link #of(Iterable)} instead.
234 @Deprecated(since = "11.0.0", forRemoval = true)
235 public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
239 abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
241 abstract @Nullable List<PathArgument> tryPathArguments();
243 abstract @Nullable List<PathArgument> tryReversePathArguments();
246 * Check if this instance identifier has empty path arguments, e.g. it is empty and corresponds to {@link #of()}.
248 * @return True if this instance identifier is empty, false otherwise.
250 public abstract boolean isEmpty();
253 * Return an optimized version of this identifier, useful when the identifier
254 * will be used very frequently.
256 * @return A optimized equivalent instance.
258 public abstract @NonNull YangInstanceIdentifier toOptimized();
261 * Return the conceptual parent {@link YangInstanceIdentifier}, which has
262 * one item less in {@link #getPathArguments()}.
264 * @return Parent {@link YangInstanceIdentifier}, or null if this object is {@link #of()}.
266 public abstract @Nullable YangInstanceIdentifier getParent();
269 * Return the conceptual parent {@link YangInstanceIdentifier}, which has one item less in
270 * {@link #getPathArguments()}.
272 * @return Parent {@link YangInstanceIdentifier}
273 * @throws VerifyException if this object is {@link #of()}.
275 public abstract @NonNull YangInstanceIdentifier coerceParent();
278 * Return the ancestor {@link YangInstanceIdentifier} with a particular depth, e.g. number of path arguments.
280 * @param depth Ancestor depth
281 * @return Ancestor {@link YangInstanceIdentifier}
282 * @throws IllegalArgumentException if the specified depth is negative or is greater than the depth of this object.
284 public abstract @NonNull YangInstanceIdentifier getAncestor(int depth);
287 * Returns an ordered iteration of path arguments.
289 * @return Immutable iteration of path arguments.
291 public abstract @NonNull List<PathArgument> getPathArguments();
294 * Returns an iterable of path arguments in reverse order. This is useful
295 * when walking up a tree organized this way.
297 * @return Immutable iterable of path arguments in reverse order.
299 public abstract @NonNull List<PathArgument> getReversePathArguments();
302 * Returns the last PathArgument. This is equivalent of iterating
303 * to the last element of the iterable returned by {@link #getPathArguments()}.
305 * @return The last past argument, or null if there are no PathArguments.
307 public abstract PathArgument getLastPathArgument();
310 * Create a {@link YangInstanceIdentifier} by taking a snapshot of provided path and iterating it backwards.
312 * @param pathTowardsRoot Path towards root
313 * @return A {@link YangInstanceIdentifier} instance
314 * @throws NullPointerException if {@code pathTowardsRoot} or any of its members is null
316 public static @NonNull YangInstanceIdentifier createReverse(final Deque<PathArgument> pathTowardsRoot) {
317 final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
318 pathTowardsRoot.size());
319 pathTowardsRoot.descendingIterator().forEachRemaining(builder::add);
320 return YangInstanceIdentifier.of(builder.build());
324 * Create a {@link YangInstanceIdentifier} by walking specified stack backwards and extracting path components
327 * @param stackTowardsRoot Stack towards root,
328 * @return A {@link YangInstanceIdentifier} instance
329 * @throws NullPointerException if {@code pathTowardsRoot} is null
331 public static <T> @NonNull YangInstanceIdentifier createReverse(final Deque<? extends T> stackTowardsRoot,
332 final Function<T, PathArgument> function) {
333 final var builder = ImmutableList.<PathArgument>builderWithExpectedSize(stackTowardsRoot.size());
334 final var it = stackTowardsRoot.descendingIterator();
335 while (it.hasNext()) {
336 builder.add(function.apply(it.next()));
338 return YangInstanceIdentifier.of(builder.build());
341 boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
342 return getPathArguments().equals(other.getPathArguments());
346 public final boolean equals(final Object obj) {
347 return this == obj || obj instanceof YangInstanceIdentifier other && pathArgumentsEqual(other);
351 * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments.
353 * @param name QName of {@link NodeIdentifier}
354 * @return Instance Identifier with additional path argument added to the end.
356 public final @NonNull YangInstanceIdentifier node(final QName name) {
357 return node(new NodeIdentifier(name));
361 * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments.
363 * @param arg Path argument which should be added to the end
364 * @return Instance Identifier with additional path argument added to the end.
366 public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
367 return new StackedYangInstanceIdentifier(this, arg);
371 * Get the relative path from an ancestor. This method attempts to perform
372 * the reverse of concatenating a base (ancestor) and a path.
375 * Ancestor against which the relative path should be calculated
376 * @return This object's relative path from parent, or Optional.absent() if
377 * the specified parent is not in fact an ancestor of this object.
379 public Optional<YangInstanceIdentifier> relativeTo(final YangInstanceIdentifier ancestor) {
380 if (this == ancestor) {
381 return Optional.of(of());
383 if (ancestor.isEmpty()) {
384 return Optional.of(this);
387 final Iterator<PathArgument> lit = getPathArguments().iterator();
390 for (PathArgument element : ancestor.getPathArguments()) {
391 // Ancestor is not really an ancestor
392 if (!lit.hasNext() || !lit.next().equals(element)) {
393 return Optional.empty();
400 return Optional.of(this);
402 if (!lit.hasNext()) {
403 return Optional.of(of());
406 return Optional.of(createRelativeIdentifier(common));
410 public final boolean contains(final YangInstanceIdentifier other) {
415 checkArgument(other != null, "other should not be null");
416 final Iterator<PathArgument> oit = other.getPathArguments().iterator();
418 for (PathArgument element : getPathArguments()) {
419 if (!oit.hasNext()) {
423 if (!element.equals(oit.next())) {
432 public final String toString() {
434 * The toStringCache is safe, since the object contract requires
435 * immutability of the object and all objects referenced from this
437 * Used lists, maps are immutable. Path Arguments (elements) are also
438 * immutable, since the PathArgument contract requires immutability.
439 * The cache is thread-safe - if multiple computations occurs at the
440 * same time, cache will be overwritten with same result.
442 final String ret = (String) TO_STRING_CACHE.getAcquire(this);
443 return ret != null ? ret : loadToString();
446 private String loadToString() {
447 final StringBuilder builder = new StringBuilder("/");
448 PathArgument prev = null;
449 for (PathArgument argument : getPathArguments()) {
453 builder.append(argument.toRelativeString(prev));
457 final String ret = builder.toString();
458 final String witness = (String) TO_STRING_CACHE.compareAndExchangeRelease(this, null, ret);
459 return witness == null ? ret : witness;
463 public final int hashCode() {
465 * The caching is safe, since the object contract requires
466 * immutability of the object and all objects referenced from this
468 * Used lists, maps are immutable. Path Arguments (elements) are also
469 * immutable, since the PathArgument contract requires immutability.
471 final int local = (int) HASH.getAcquire(this);
472 return local != 0 ? local : loadHashCode();
475 private static int hashCode(final Object value) {
480 if (byte[].class.equals(value.getClass())) {
481 return Arrays.hashCode((byte[]) value);
484 if (value.getClass().isArray()) {
486 int length = Array.getLength(value);
487 for (int i = 0; i < length; i++) {
488 hash += Objects.hashCode(Array.get(value, i));
494 return Objects.hashCode(value);
497 private int loadHashCode() {
498 final int computed = computeHashCode();
499 HASH.setRelease(this, computed);
503 abstract int computeHashCode();
506 final Object writeReplace() {
507 return new YIDv1(this);
511 * Returns new builder for InstanceIdentifier with empty path arguments.
513 * @return new builder for InstanceIdentifier with empty path arguments.
515 public static @NonNull InstanceIdentifierBuilder builder() {
516 return new YangInstanceIdentifierBuilder();
520 * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
522 * @param origin InstanceIdentifier from which path arguments are copied.
523 * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
525 public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
526 return new YangInstanceIdentifierBuilder(origin.getPathArguments());
530 * Path argument / component of InstanceIdentifier.
531 * Path argument uniquely identifies node in data tree on particular
535 * This interface itself is used as common parent for actual
536 * path arguments types and should not be implemented by user code.
539 * Path arguments SHOULD contain only minimum of information
540 * required to uniquely identify node on particular subtree level.
543 * For actual path arguments types see:
545 * <li>{@link NodeIdentifier} - Identifier of container or leaf
546 * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
547 * <li>{@link NodeWithValue} - Identifier of leaf-list entry
550 public abstract static sealed class PathArgument implements Identifier, Comparable<PathArgument> {
552 private static final long serialVersionUID = -4546547994250849340L;
554 private final @NonNull QName nodeType;
555 private transient volatile int hashValue;
557 protected PathArgument(final QName nodeType) {
558 this.nodeType = requireNonNull(nodeType);
562 * Returns unique QName of data node as defined in YANG Schema, if available.
566 public final @NonNull QName getNodeType() {
571 * Return the string representation of this object for use in context
572 * provided by a previous object. This method can be implemented in
573 * terms of {@link #toString()}, but implementations are encourage to
574 * reuse any context already emitted by the previous object.
576 * @param previous Previous path argument
577 * @return String representation
579 public @NonNull String toRelativeString(final PathArgument previous) {
580 if (previous != null && nodeType.getModule().equals(previous.nodeType.getModule())) {
581 return nodeType.getLocalName();
583 return nodeType.toString();
587 @SuppressWarnings("checkstyle:parameterName")
588 public int compareTo(final PathArgument o) {
589 return nodeType.compareTo(o.nodeType);
592 protected int hashCodeImpl() {
593 return nodeType.hashCode();
597 public final int hashCode() {
599 return (local = hashValue) != 0 ? local : (hashValue = hashCodeImpl());
603 public boolean equals(final Object obj) {
607 if (obj == null || this.getClass() != obj.getClass()) {
611 return nodeType.equals(((PathArgument) obj).nodeType);
615 public String toString() {
616 return nodeType.toString();
620 abstract Object writeReplace();
624 * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
625 * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
627 public static final class NodeIdentifier extends PathArgument {
629 private static final long serialVersionUID = -2255888212390871347L;
630 private static final LoadingCache<QName, NodeIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
631 .build(new CacheLoader<QName, NodeIdentifier>() {
633 public NodeIdentifier load(final QName key) {
634 return new NodeIdentifier(key);
638 public NodeIdentifier(final QName node) {
643 * Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
644 * instance cache, resulting in object reuse for equal inputs.
646 * @param node Node's QName
647 * @return A {@link NodeIdentifier}
649 public static @NonNull NodeIdentifier create(final QName node) {
650 return CACHE.getUnchecked(node);
654 Object writeReplace() {
655 return new NIv1(this);
660 * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
663 public abstract static sealed class NodeIdentifierWithPredicates extends PathArgument {
665 public static final class Singleton extends NodeIdentifierWithPredicates {
667 private static final long serialVersionUID = 1L;
669 private final @NonNull QName key;
670 private final @NonNull Object value;
672 Singleton(final QName node, final QName key, final Object value) {
674 this.key = requireNonNull(key);
675 this.value = requireNonNull(value);
679 public SingletonSet<Entry<QName, Object>> entrySet() {
680 return SingletonSet.of(singleEntry());
684 public SingletonSet<QName> keySet() {
685 return SingletonSet.of(key);
689 public boolean containsKey(final QName qname) {
690 return key.equals(requireNonNull(qname));
694 public SingletonSet<Object> values() {
695 return SingletonSet.of(value);
704 public ImmutableMap<QName, Object> asMap() {
705 return ImmutableMap.of(key, value);
709 * Return the single entry contained in this object. This is equivalent to
710 * {@code entrySet().iterator().next()}.
712 * @return A single entry.
714 public @NonNull Entry<QName, Object> singleEntry() {
715 return new SimpleImmutableEntry<>(key, value);
719 boolean equalMapping(final NodeIdentifierWithPredicates other) {
720 final Singleton single = (Singleton) other;
721 return key.equals(single.key) && Objects.deepEquals(value, single.value);
725 Object keyValue(final QName qname) {
726 return key.equals(qname) ? value : null;
730 private static final class Regular extends NodeIdentifierWithPredicates {
732 private static final long serialVersionUID = 1L;
734 private final @NonNull Map<QName, Object> keyValues;
736 Regular(final QName node, final Map<QName, Object> keyValues) {
738 this.keyValues = requireNonNull(keyValues);
742 public Set<Entry<QName, Object>> entrySet() {
743 return keyValues.entrySet();
747 public Set<QName> keySet() {
748 return keyValues.keySet();
752 public boolean containsKey(final QName qname) {
753 return keyValues.containsKey(requireNonNull(qname));
757 public Collection<Object> values() {
758 return keyValues.values();
763 return keyValues.size();
767 public Map<QName, Object> asMap() {
772 Object keyValue(final QName qname) {
773 return keyValues.get(qname);
777 boolean equalMapping(final NodeIdentifierWithPredicates other) {
778 final Map<QName, Object> otherKeyValues = ((Regular) other).keyValues;
779 // TODO: benchmark to see if just calling equals() on the two maps is not faster
780 if (keyValues == otherKeyValues) {
783 if (keyValues.size() != otherKeyValues.size()) {
787 for (Entry<QName, Object> entry : entrySet()) {
788 final Object otherValue = otherKeyValues.get(entry.getKey());
789 if (otherValue == null || !Objects.deepEquals(entry.getValue(), otherValue)) {
799 private static final long serialVersionUID = -4787195606494761540L;
801 NodeIdentifierWithPredicates(final QName node) {
805 public static @NonNull NodeIdentifierWithPredicates of(final QName node) {
806 return new Regular(node, ImmutableMap.of());
809 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) {
810 return new Singleton(node, key, value);
813 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Entry<QName, Object> entry) {
814 return of(node, entry.getKey(), entry.getValue());
817 public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map<QName, Object> keyValues) {
818 return keyValues.size() == 1 ? of(keyValues, node)
819 // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
820 : new Regular(node, ImmutableOffsetMap.unorderedCopyOf(keyValues));
823 public static @NonNull NodeIdentifierWithPredicates of(final QName node,
824 final ImmutableOffsetMap<QName, Object> keyValues) {
825 return keyValues.size() == 1 ? of(keyValues, node) : new Regular(node, keyValues);
828 private static @NonNull NodeIdentifierWithPredicates of(final Map<QName, Object> keyValues, final QName node) {
829 return of(node, keyValues.entrySet().iterator().next());
833 * Return the set of predicates keys and values. Keys are guaranteeed to be unique.
835 * @return Predicate set.
837 public abstract @NonNull Set<Entry<QName, Object>> entrySet();
840 * Return the predicate key in the iteration order of {@link #entrySet()}.
842 * @return Predicate values.
844 public abstract @NonNull Set<QName> keySet();
847 * Determine whether a particular predicate key is present.
849 * @param key Predicate key
850 * @return True if the predicate is present, false otherwise
851 * @throws NullPointerException if {@code key} is null
853 public abstract boolean containsKey(QName key);
856 * Return the predicate values in the iteration order of {@link #entrySet()}.
858 * @return Predicate values.
860 public abstract @NonNull Collection<Object> values();
863 public final @Nullable Object getValue(final QName key) {
864 return keyValue(requireNonNull(key));
868 public final <T> @Nullable T getValue(final QName key, final Class<T> valueClass) {
869 return valueClass.cast(getValue(key));
873 * Return the number of predicates present.
875 * @return The number of predicates present.
877 public abstract int size();
880 * A Map-like view of this identifier's predicates. The view is expected to be stable and effectively-immutable.
882 * @return Map of predicates.
885 public abstract @NonNull Map<QName, Object> asMap();
888 protected final int hashCodeImpl() {
889 int result = 31 * super.hashCodeImpl();
890 for (Entry<QName, Object> entry : entrySet()) {
891 result += entry.getKey().hashCode() + YangInstanceIdentifier.hashCode(entry.getValue());
897 @SuppressWarnings("checkstyle:equalsHashCode")
898 public final boolean equals(final Object obj) {
899 return super.equals(obj) && equalMapping((NodeIdentifierWithPredicates) obj);
902 abstract boolean equalMapping(NodeIdentifierWithPredicates other);
904 abstract @Nullable Object keyValue(@NonNull QName qname);
907 public final String toString() {
908 return super.toString() + '[' + asMap() + ']';
912 public final String toRelativeString(final PathArgument previous) {
913 return super.toRelativeString(previous) + '[' + asMap() + ']';
917 final Object writeReplace() {
918 return new NIPv2(this);
923 * Simple path argument identifying a {@link LeafSetEntryNode} leaf
926 public static final class NodeWithValue<T> extends PathArgument {
928 private static final long serialVersionUID = -3637456085341738431L;
930 private final @NonNull T value;
932 public NodeWithValue(final QName node, final T value) {
934 this.value = requireNonNull(value);
937 public @NonNull T getValue() {
942 protected int hashCodeImpl() {
943 return 31 * super.hashCodeImpl() + YangInstanceIdentifier.hashCode(value);
947 @SuppressWarnings("checkstyle:equalsHashCode")
948 public boolean equals(final Object obj) {
949 if (!super.equals(obj)) {
952 final NodeWithValue<?> other = (NodeWithValue<?>) obj;
953 return Objects.deepEquals(value, other.value);
957 public String toString() {
958 return super.toString() + '[' + value + ']';
962 public String toRelativeString(final PathArgument previous) {
963 return super.toRelativeString(previous) + '[' + value + ']';
967 Object writeReplace() {
968 return new NIVv1(this);
973 * Fluent Builder of Instance Identifier instances.
975 public interface InstanceIdentifierBuilder extends Mutable {
977 * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
979 * @param arg A {@link PathArgument} to be added
980 * @return this builder
982 @NonNull InstanceIdentifierBuilder node(PathArgument arg);
985 * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
987 * @param nodeType QName of {@link NodeIdentifier} which will be added
988 * @return this builder
990 @NonNull InstanceIdentifierBuilder node(QName nodeType);
993 * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
994 * instance identifier.
996 * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
997 * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
998 * @return this builder
1000 @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
1003 * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
1005 * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
1006 * @param key QName of key which will be added
1007 * @param value value of key which will be added
1008 * @return this builder
1010 @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
1013 * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
1015 * @param args {@link PathArgument}s to be added
1016 * @return this builder
1017 * @throws NullPointerException if any of the arguments is null
1019 @NonNull InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
1022 * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
1024 * @param args {@link PathArgument}s to be added
1025 * @return this builder
1026 * @throws NullPointerException if any of the arguments is null
1028 default @NonNull InstanceIdentifierBuilder append(final PathArgument... args) {
1029 return append(Arrays.asList(args));
1033 * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
1035 * @return {@link YangInstanceIdentifier}
1037 @NonNull YangInstanceIdentifier build();