Remove references to YangInstanceIdentifier.EMPTY
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / YangInstanceIdentifier.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.api;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
13
14 import com.google.common.annotations.Beta;
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.ImmutableSet;
21 import com.google.common.collect.Iterables;
22 import com.google.common.collect.Sets;
23 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
24 import java.io.Serializable;
25 import java.lang.reflect.Array;
26 import java.util.AbstractMap.SimpleImmutableEntry;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Deque;
31 import java.util.Iterator;
32 import java.util.List;
33 import java.util.Map;
34 import java.util.Map.Entry;
35 import java.util.Objects;
36 import java.util.Optional;
37 import java.util.Set;
38 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
39 import java.util.function.Function;
40 import org.eclipse.jdt.annotation.NonNull;
41 import org.eclipse.jdt.annotation.Nullable;
42 import org.opendaylight.yangtools.concepts.Builder;
43 import org.opendaylight.yangtools.concepts.Immutable;
44 import org.opendaylight.yangtools.concepts.Path;
45 import org.opendaylight.yangtools.util.HashCodeBuilder;
46 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
47 import org.opendaylight.yangtools.util.SharedSingletonMap;
48 import org.opendaylight.yangtools.util.SingletonSet;
49 import org.opendaylight.yangtools.yang.common.QName;
50 import org.opendaylight.yangtools.yang.common.QNameModule;
51 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
52
53 /**
54  * Unique identifier of a particular node instance in the data tree.
55  *
56  * <p>
57  * Java representation of YANG Built-in type <code>instance-identifier</code>,
58  * which conceptually is XPath expression minimized to uniquely identify element
59  * in data tree which conforms to constraints maintained by YANG Model,
60  * effectively this makes Instance Identifier a path to element in data tree.
61  *
62  * <p>
63  * Constraints put in YANG specification on instance-identifier allowed it to be
64  * effectively represented in Java and it's evaluation does not require
65  * full-blown XPath processor.
66  *
67  * <p>
68  * <h3>Path Arguments</h3>
69  * Path to the node represented in instance identifier consists of
70  * {@link PathArgument} which carries necessary information to uniquely identify
71  * node on particular level in the subtree.
72  *
73  * <ul>
74  * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality
75  * <code>0..1</code> in particular subtree in data tree.</li>
76  * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item),
77  * which has cardinality <code>0..n</code>.</li>
78  * <li>{@link NodeWithValue} - Identifier of instance <code>leaf</code> node or
79  * <code>leaf-list</code> node.</li>
80  * <li>{@link AugmentationIdentifier} - Identifier of instance of
81  * <code>augmentation</code> node.</li>
82  * </ul>
83  *
84  * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
85  */
86 // FIXME: 4.0.0: this concept needs to be moved to yang-common, as parser components need the ability to refer
87 //               to data nodes -- most notably XPath expressions and {@code default} statement arguments need to be able
88 //               to represent these.
89 // FIXME: Remove this suppression when we remove EMPTY
90 @SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION",
91     justification = "Having EMPTY a constant is the trouble here")
92 public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
93     /**
94      * An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG namespace.
95      *
96      * @deprecated Use {@link #empty()} instead.
97      */
98     @Deprecated(forRemoval = true)
99     public static final @NonNull YangInstanceIdentifier EMPTY = FixedYangInstanceIdentifier.EMPTY_INSTANCE;
100
101     private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
102             AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
103     private static final long serialVersionUID = 4L;
104
105     private final int hash;
106     private transient volatile String toStringCache = null;
107
108     // Package-private to prevent outside subclassing
109     YangInstanceIdentifier(final int hash) {
110         this.hash = hash;
111     }
112
113     /**
114      * Return An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual root of the YANG
115      * namespace.
116      *
117      * @return An empty YangInstanceIdentifier
118      */
119     public static @NonNull YangInstanceIdentifier empty() {
120         return FixedYangInstanceIdentifier.EMPTY_INSTANCE;
121     }
122
123     abstract @NonNull YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
124
125     abstract @Nullable Collection<PathArgument> tryPathArguments();
126
127     abstract @Nullable Collection<PathArgument> tryReversePathArguments();
128
129     /**
130      * Check if this instance identifier has empty path arguments, e.g. it is
131      * empty and corresponds to {@link #empty()}.
132      *
133      * @return True if this instance identifier is empty, false otherwise.
134      */
135     public abstract boolean isEmpty();
136
137     /**
138      * Return an optimized version of this identifier, useful when the identifier
139      * will be used very frequently.
140      *
141      * @return A optimized equivalent instance.
142      */
143     @Beta
144     public abstract @NonNull YangInstanceIdentifier toOptimized();
145
146     /**
147      * Return the conceptual parent {@link YangInstanceIdentifier}, which has
148      * one item less in {@link #getPathArguments()}.
149      *
150      * @return Parent {@link YangInstanceIdentifier}, or null if this object is {@link #empty()}.
151      */
152     public abstract @Nullable YangInstanceIdentifier getParent();
153
154     /**
155      * Return the ancestor {@link YangInstanceIdentifier} with a particular depth, e.g. number of path arguments.
156      *
157      * @param depth Ancestor depth
158      * @return Ancestor {@link YangInstanceIdentifier}
159      * @throws IllegalArgumentException if the specified depth is negative or is greater than the depth of this object.
160      */
161     public abstract @NonNull YangInstanceIdentifier getAncestor(int depth);
162
163     /**
164      * Returns an ordered iteration of path arguments.
165      *
166      * @return Immutable iteration of path arguments.
167      */
168     public abstract @NonNull List<PathArgument> getPathArguments();
169
170     /**
171      * Returns an iterable of path arguments in reverse order. This is useful
172      * when walking up a tree organized this way.
173      *
174      * @return Immutable iterable of path arguments in reverse order.
175      */
176     public abstract @NonNull List<PathArgument> getReversePathArguments();
177
178     /**
179      * Returns the last PathArgument. This is equivalent of iterating
180      * to the last element of the iterable returned by {@link #getPathArguments()}.
181      *
182      * @return The last past argument, or null if there are no PathArguments.
183      */
184     public abstract PathArgument getLastPathArgument();
185
186     public static @NonNull YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
187         if (Iterables.isEmpty(path)) {
188             return empty();
189         }
190
191         final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
192         for (PathArgument a : path) {
193             hash.addArgument(a);
194         }
195
196         return FixedYangInstanceIdentifier.create(path, hash.build());
197     }
198
199     public static @NonNull YangInstanceIdentifier create(final PathArgument... path) {
200         // We are forcing a copy, since we cannot trust the user
201         return create(Arrays.asList(path));
202     }
203
204     /**
205      * Create a {@link YangInstanceIdentifier} by taking a snapshot of provided path and iterating it backwards.
206      *
207      * @param pathTowardsRoot Path towards root
208      * @return A {@link YangInstanceIdentifier} instance
209      * @throws NullPointerException if {@code pathTowardsRoot} or any of its members is null
210      */
211     public static @NonNull YangInstanceIdentifier createReverse(final Deque<PathArgument> pathTowardsRoot) {
212         final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
213             pathTowardsRoot.size());
214         pathTowardsRoot.descendingIterator().forEachRemaining(builder::add);
215         return YangInstanceIdentifier.create(builder.build());
216     }
217
218     /**
219      * Create a {@link YangInstanceIdentifier} by walking specified stack backwards and extracting path components
220      * from it.
221      *
222      * @param stackTowardsRoot Stack towards root,
223      * @return A {@link YangInstanceIdentifier} instance
224      * @throws NullPointerException if {@code pathTowardsRoot} is null
225      */
226     public static <T> @NonNull YangInstanceIdentifier createReverse(final Deque<? extends T> stackTowardsRoot,
227             final Function<T, PathArgument> function) {
228         final ImmutableList.Builder<PathArgument> builder = ImmutableList.builderWithExpectedSize(
229             stackTowardsRoot.size());
230         final Iterator<? extends T> it = stackTowardsRoot.descendingIterator();
231         while (it.hasNext()) {
232             builder.add(function.apply(it.next()));
233         }
234         return YangInstanceIdentifier.create(builder.build());
235     }
236
237     boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
238         return Iterables.elementsEqual(getPathArguments(), other.getPathArguments());
239     }
240
241     @Override
242     public boolean equals(final Object obj) {
243         if (this == obj) {
244             return true;
245         }
246         if (!(obj instanceof YangInstanceIdentifier)) {
247             return false;
248         }
249         YangInstanceIdentifier other = (YangInstanceIdentifier) obj;
250         if (this.hashCode() != obj.hashCode()) {
251             return false;
252         }
253
254         return pathArgumentsEqual(other);
255     }
256
257     /**
258      * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments.
259      *
260      * @param name QName of {@link NodeIdentifier}
261      * @return Instance Identifier with additional path argument added to the end.
262      */
263     public final @NonNull YangInstanceIdentifier node(final QName name) {
264         return node(new NodeIdentifier(name));
265     }
266
267     /**
268      * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments.
269      *
270      * @param arg Path argument which should be added to the end
271      * @return Instance Identifier with additional path argument added to the end.
272      */
273     public final @NonNull YangInstanceIdentifier node(final PathArgument arg) {
274         return new StackedYangInstanceIdentifier(this, arg, HashCodeBuilder.nextHashCode(hash, arg));
275     }
276
277     /**
278      * Get the relative path from an ancestor. This method attempts to perform
279      * the reverse of concatenating a base (ancestor) and a path.
280      *
281      * @param ancestor
282      *            Ancestor against which the relative path should be calculated
283      * @return This object's relative path from parent, or Optional.absent() if
284      *         the specified parent is not in fact an ancestor of this object.
285      */
286     public Optional<YangInstanceIdentifier> relativeTo(final YangInstanceIdentifier ancestor) {
287         if (this == ancestor) {
288             return Optional.of(empty());
289         }
290         if (ancestor.isEmpty()) {
291             return Optional.of(this);
292         }
293
294         final Iterator<PathArgument> lit = getPathArguments().iterator();
295         final Iterator<PathArgument> oit = ancestor.getPathArguments().iterator();
296         int common = 0;
297
298         while (oit.hasNext()) {
299             // Ancestor is not really an ancestor
300             if (!lit.hasNext() || !lit.next().equals(oit.next())) {
301                 return Optional.empty();
302             }
303
304             ++common;
305         }
306
307         if (common == 0) {
308             return Optional.of(this);
309         }
310         if (!lit.hasNext()) {
311             return Optional.of(empty());
312         }
313
314         return Optional.of(createRelativeIdentifier(common));
315     }
316
317     @Override
318     public final boolean contains(final YangInstanceIdentifier other) {
319         if (this == other) {
320             return true;
321         }
322
323         checkArgument(other != null, "other should not be null");
324         final Iterator<PathArgument> lit = getPathArguments().iterator();
325         final Iterator<PathArgument> oit = other.getPathArguments().iterator();
326
327         while (lit.hasNext()) {
328             if (!oit.hasNext()) {
329                 return false;
330             }
331
332             if (!lit.next().equals(oit.next())) {
333                 return false;
334             }
335         }
336
337         return true;
338     }
339
340     @Override
341     public final String toString() {
342         /*
343          * The toStringCache is safe, since the object contract requires
344          * immutability of the object and all objects referenced from this
345          * object.
346          * Used lists, maps are immutable. Path Arguments (elements) are also
347          * immutable, since the PathArgument contract requires immutability.
348          * The cache is thread-safe - if multiple computations occurs at the
349          * same time, cache will be overwritten with same result.
350          */
351         String ret = toStringCache;
352         if (ret == null) {
353             final StringBuilder builder = new StringBuilder("/");
354             PathArgument prev = null;
355             for (PathArgument argument : getPathArguments()) {
356                 if (prev != null) {
357                     builder.append('/');
358                 }
359                 builder.append(argument.toRelativeString(prev));
360                 prev = argument;
361             }
362
363             ret = builder.toString();
364             TOSTRINGCACHE_UPDATER.lazySet(this, ret);
365         }
366         return ret;
367     }
368
369     @Override
370     public final int hashCode() {
371         /*
372          * The caching is safe, since the object contract requires
373          * immutability of the object and all objects referenced from this
374          * object.
375          * Used lists, maps are immutable. Path Arguments (elements) are also
376          * immutable, since the PathArgument contract requires immutability.
377          */
378         return hash;
379     }
380
381     @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD",
382             justification = "https://github.com/spotbugs/spotbugs/issues/811")
383     private static int hashCode(final Object value) {
384         if (value == null) {
385             return 0;
386         }
387
388         if (byte[].class.equals(value.getClass())) {
389             return Arrays.hashCode((byte[]) value);
390         }
391
392         if (value.getClass().isArray()) {
393             int hash = 0;
394             int length = Array.getLength(value);
395             for (int i = 0; i < length; i++) {
396                 hash += Objects.hashCode(Array.get(value, i));
397             }
398
399             return hash;
400         }
401
402         return Objects.hashCode(value);
403     }
404
405     final Object writeReplace() {
406         return new YIDv1(this);
407     }
408
409     // Static factories & helpers
410
411     /**
412      * Returns a new InstanceIdentifier with only one path argument of type {@link NodeIdentifier} with supplied
413      * QName.
414      *
415      * @param name QName of first node identifier
416      * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
417      */
418     public static @NonNull YangInstanceIdentifier of(final QName name) {
419         return create(new NodeIdentifier(name));
420     }
421
422     /**
423      * Returns new builder for InstanceIdentifier with empty path arguments.
424      *
425      * @return new builder for InstanceIdentifier with empty path arguments.
426      */
427     public static @NonNull InstanceIdentifierBuilder builder() {
428         return new YangInstanceIdentifierBuilder();
429     }
430
431     /**
432      * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
433      *
434      * @param origin InstanceIdentifier from which path arguments are copied.
435      * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
436      */
437     public static @NonNull InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
438         return new YangInstanceIdentifierBuilder(origin.getPathArguments(), origin.hashCode());
439     }
440
441     /**
442      * Path argument / component of InstanceIdentifier.
443      * Path argument uniquely identifies node in data tree on particular
444      * level.
445      *
446      * <p>
447      * This interface itself is used as common parent for actual
448      * path arguments types and should not be implemented by user code.
449      *
450      * <p>
451      * Path arguments SHOULD contain only minimum of information
452      * required to uniquely identify node on particular subtree level.
453      *
454      * <p>
455      * For actual path arguments types see:
456      * <ul>
457      * <li>{@link NodeIdentifier} - Identifier of container or leaf
458      * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
459      * <li>{@link AugmentationIdentifier} - Identifier of augmentation
460      * <li>{@link NodeWithValue} - Identifier of leaf-list entry
461      * </ul>
462      */
463     public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
464         /**
465          * Returns unique QName of data node as defined in YANG Schema, if available.
466          *
467          * @return Node type
468          * @throws UnsupportedOperationException if node type is not applicable, for example in case of an augmentation.
469          */
470         @NonNull QName getNodeType();
471
472         /**
473          * Return the string representation of this object for use in context
474          * provided by a previous object. This method can be implemented in
475          * terms of {@link #toString()}, but implementations are encourage to
476          * reuse any context already emitted by the previous object.
477          *
478          * @param previous Previous path argument
479          * @return String representation
480          */
481         @NonNull String toRelativeString(PathArgument previous);
482     }
483
484     private abstract static class AbstractPathArgument implements PathArgument {
485         private static final long serialVersionUID = -4546547994250849340L;
486         private final @NonNull QName nodeType;
487         private transient volatile int hashValue;
488
489         protected AbstractPathArgument(final QName nodeType) {
490             this.nodeType = requireNonNull(nodeType);
491         }
492
493         @Override
494         public final QName getNodeType() {
495             return nodeType;
496         }
497
498         @Override
499         @SuppressWarnings("checkstyle:parameterName")
500         public int compareTo(final PathArgument o) {
501             return nodeType.compareTo(o.getNodeType());
502         }
503
504         protected int hashCodeImpl() {
505             return 31 + getNodeType().hashCode();
506         }
507
508         @Override
509         public final int hashCode() {
510             int local;
511             return (local = hashValue) != 0 ? local : (hashValue = hashCodeImpl());
512         }
513
514         @Override
515         public boolean equals(final Object obj) {
516             if (this == obj) {
517                 return true;
518             }
519             if (obj == null || this.getClass() != obj.getClass()) {
520                 return false;
521             }
522
523             return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
524         }
525
526         @Override
527         public String toString() {
528             return getNodeType().toString();
529         }
530
531         @Override
532         public String toRelativeString(final PathArgument previous) {
533             if (previous instanceof AbstractPathArgument) {
534                 final QNameModule mod = previous.getNodeType().getModule();
535                 if (getNodeType().getModule().equals(mod)) {
536                     return getNodeType().getLocalName();
537                 }
538             }
539
540             return getNodeType().toString();
541         }
542
543         abstract Object writeReplace();
544     }
545
546     /**
547      * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
548      * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
549      */
550     public static final class NodeIdentifier extends AbstractPathArgument {
551         private static final long serialVersionUID = -2255888212390871347L;
552         private static final LoadingCache<QName, NodeIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
553                 .build(new CacheLoader<QName, NodeIdentifier>() {
554                     @Override
555                     public NodeIdentifier load(final QName key) {
556                         return new NodeIdentifier(key);
557                     }
558                 });
559
560         public NodeIdentifier(final QName node) {
561             super(node);
562         }
563
564         /**
565          * Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
566          * instance cache, resulting in object reuse for equal inputs.
567          *
568          * @param node Node's QName
569          * @return A {@link NodeIdentifier}
570          */
571         public static @NonNull NodeIdentifier create(final QName node) {
572             return CACHE.getUnchecked(node);
573         }
574
575         @Override
576         Object writeReplace() {
577             return new NIv1(this);
578         }
579     }
580
581     /**
582      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
583      * overall data tree.
584      */
585     public abstract static class NodeIdentifierWithPredicates extends AbstractPathArgument {
586         private static final class Singleton extends NodeIdentifierWithPredicates {
587             private static final long serialVersionUID = 1L;
588
589             private final @NonNull QName key;
590             private final @NonNull Object value;
591
592             Singleton(final QName node, final QName key, final Object value) {
593                 super(node);
594                 this.key = requireNonNull(key);
595                 this.value = requireNonNull(value);
596             }
597
598             @Override
599             public SingletonSet<Entry<QName, Object>> entrySet() {
600                 return SingletonSet.of(new SimpleImmutableEntry<>(key, value));
601             }
602
603             @Override
604             public SingletonSet<QName> keySet() {
605                 return SingletonSet.of(key);
606             }
607
608             @Override
609             public SingletonSet<Object> values() {
610                 return SingletonSet.of(value);
611             }
612
613             @Override
614             public int size() {
615                 return 1;
616             }
617
618             @Override
619             public ImmutableMap<QName, Object> asMap() {
620                 return ImmutableMap.of(key, value);
621             }
622
623             @Override
624             boolean equalMapping(final NodeIdentifierWithPredicates other) {
625                 final Singleton single = (Singleton) other;
626                 return key.equals(single.key) && Objects.deepEquals(value, single.value);
627             }
628
629             @Override
630             Object keyValue(final QName qname) {
631                 return key.equals(qname) ? value : null;
632             }
633         }
634
635         private static final class Regular extends NodeIdentifierWithPredicates {
636             private static final long serialVersionUID = 1L;
637
638             private final @NonNull Map<QName, Object> keyValues;
639
640             Regular(final QName node, final Map<QName, Object> keyValues) {
641                 super(node);
642                 this.keyValues = requireNonNull(keyValues);
643             }
644
645             @Override
646             public Set<Entry<QName, Object>> entrySet() {
647                 return keyValues.entrySet();
648             }
649
650             @Override
651             public Set<QName> keySet() {
652                 return keyValues.keySet();
653             }
654
655             @Override
656             public Collection<Object> values() {
657                 return keyValues.values();
658             }
659
660             @Override
661             public int size() {
662                 return keyValues.size();
663             }
664
665             @Override
666             public Map<QName, Object> asMap() {
667                 return keyValues;
668             }
669
670             @Override
671             Object keyValue(final QName qname) {
672                 return keyValues.get(qname);
673             }
674
675             @Override
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) {
680                     return true;
681                 }
682                 if (keyValues.size() != otherKeyValues.size()) {
683                     return false;
684                 }
685
686                 for (Entry<QName, Object> entry : entrySet()) {
687                     final Object otherValue = otherKeyValues.get(entry.getKey());
688                     if (otherValue == null || !Objects.deepEquals(entry.getValue(), otherValue)) {
689                         return false;
690                     }
691                 }
692
693                 return true;
694             }
695         }
696
697         private static final long serialVersionUID = -4787195606494761540L;
698
699         NodeIdentifierWithPredicates(final QName node) {
700             super(node);
701         }
702
703         public static @NonNull NodeIdentifierWithPredicates of(final QName node) {
704             return new Regular(node, ImmutableMap.of());
705         }
706
707         public static @NonNull NodeIdentifierWithPredicates of(final QName node, final QName key, final Object value) {
708             return new Singleton(node, key, value);
709         }
710
711         public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Entry<QName, Object> entry) {
712             return of(node, entry.getKey(), entry.getValue());
713         }
714
715         public static @NonNull NodeIdentifierWithPredicates of(final QName node, final Map<QName, Object> keyValues) {
716             return keyValues.size() == 1 ? of(keyValues, node)
717                     // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
718                     : new Regular(node, ImmutableOffsetMap.unorderedCopyOf(keyValues));
719         }
720
721         public static @NonNull NodeIdentifierWithPredicates of(final QName node,
722                 final ImmutableOffsetMap<QName, Object> keyValues) {
723             return keyValues.size() == 1 ? of(keyValues, node) : new Regular(node, keyValues);
724         }
725
726         @Deprecated
727         public static @NonNull NodeIdentifierWithPredicates of(final QName node,
728                 final SharedSingletonMap<QName, Object> keyValues) {
729             return of(node, keyValues.getEntry());
730         }
731
732         private static @NonNull NodeIdentifierWithPredicates of(final Map<QName, Object> keyValues, final QName node) {
733             return of(node, keyValues.entrySet().iterator().next());
734         }
735
736         /**
737          * Return the set of predicates keys and values. Keys are guaranteeed to be unique.
738          *
739          * @return Predicate set.
740          */
741         @Beta
742         public abstract @NonNull Set<Entry<QName, Object>> entrySet();
743
744         /**
745          * Return the predicate key in the iteration order of {@link #entrySet()}.
746          *
747          * @return Predicate values.
748          */
749         @Beta
750         public abstract @NonNull Set<QName> keySet();
751
752         /**
753          * Return the predicate values in the iteration order of {@link #entrySet()}.
754          *
755          * @return Predicate values.
756          */
757         @Beta
758         public abstract @NonNull Collection<Object> values();
759
760         @Beta
761         public final @Nullable Object getValue(final QName key) {
762             return keyValue(requireNonNull(key));
763         }
764
765         @Beta
766         public final <T> @Nullable T getValue(final QName key, final Class<T> valueClass) {
767             return valueClass.cast(getValue(key));
768         }
769
770         /**
771          * Return the number of predicates present.
772          *
773          * @return The number of predicates present.
774          */
775         @Beta
776         public abstract int size();
777
778         /**
779          * A Map-like view of this identifier's predicates. The view is expected to be stable and effectively-immutable.
780          *
781          * @return Map of predicates.
782          * @deprecated This method in a provisional one. It can be used in the code base, but users requiring it should
783          *             contact <a href="mailto:yangtools-dev@lists.opendaylight.org">yangtools-dev</a> for migration
784          *             guidelines. Callers are strongly encouraged to explore {@link #entrySet()}, {@link #size()},
785          *             {@link #values()} and {@link #keySet()} as an alternative.
786          */
787         @Beta
788         @Deprecated
789         // FIXME: 4.0.0: evaluate the real usefulness of this. The problem here is Map.hashCode() and Map.equals(),
790         //               which limits our options.
791         public abstract @NonNull Map<QName, Object> asMap();
792
793         @Override
794         protected final int hashCodeImpl() {
795             int result = 31 * super.hashCodeImpl();
796             for (Entry<QName, Object> entry : entrySet()) {
797                 result += entry.getKey().hashCode() + YangInstanceIdentifier.hashCode(entry.getValue());
798             }
799             return result;
800         }
801
802         @Override
803         @SuppressWarnings("checkstyle:equalsHashCode")
804         public final boolean equals(final Object obj) {
805             return super.equals(obj) && equalMapping((NodeIdentifierWithPredicates) obj);
806         }
807
808         abstract boolean equalMapping(NodeIdentifierWithPredicates other);
809
810         abstract @Nullable Object keyValue(@NonNull QName qname);
811
812         @Override
813         public final String toString() {
814             return super.toString() + '[' + asMap() + ']';
815         }
816
817         @Override
818         public final String toRelativeString(final PathArgument previous) {
819             return super.toRelativeString(previous) + '[' + asMap() + ']';
820         }
821
822         @Override
823         final Object writeReplace() {
824             return new NIPv2(this);
825         }
826     }
827
828     /**
829      * Simple path argument identifying a {@link LeafSetEntryNode} leaf
830      * overall data tree.
831      */
832     public static final class NodeWithValue<T> extends AbstractPathArgument {
833         private static final long serialVersionUID = -3637456085341738431L;
834
835         private final T value;
836
837         public NodeWithValue(final QName node, final T value) {
838             super(node);
839             this.value = value;
840         }
841
842         public T getValue() {
843             return value;
844         }
845
846         @Override
847         protected int hashCodeImpl() {
848             final int prime = 31;
849             int result = super.hashCodeImpl();
850             result = prime * result + YangInstanceIdentifier.hashCode(value);
851             return result;
852         }
853
854         @Override
855         @SuppressWarnings("checkstyle:equalsHashCode")
856         public boolean equals(final Object obj) {
857             if (!super.equals(obj)) {
858                 return false;
859             }
860             final NodeWithValue<?> other = (NodeWithValue<?>) obj;
861             return Objects.deepEquals(value, other.value);
862         }
863
864         @Override
865         public String toString() {
866             return super.toString() + '[' + value + ']';
867         }
868
869         @Override
870         public String toRelativeString(final PathArgument previous) {
871             return super.toRelativeString(previous) + '[' + value + ']';
872         }
873
874         @Override
875         Object writeReplace() {
876             return new NIVv1(this);
877         }
878     }
879
880     /**
881      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
882      * node in particular subtree.
883      *
884      * <p>
885      * Augmentation is uniquely identified by set of all possible child nodes.
886      * This is possible
887      * to identify instance of augmentation,
888      * since RFC6020 states that <code>augment</code> that augment
889      * statement must not add multiple nodes from same namespace
890      * / module to the target node.
891      *
892      * @see <a href="http://tools.ietf.org/html/rfc6020#section-7.15">RFC6020</a>
893      */
894     public static final class AugmentationIdentifier implements PathArgument {
895         private static final long serialVersionUID = -8122335594681936939L;
896
897         private static final LoadingCache<ImmutableSet<QName>, AugmentationIdentifier> CACHE = CacheBuilder.newBuilder()
898                 .weakValues().build(new CacheLoader<ImmutableSet<QName>, AugmentationIdentifier>() {
899                     @Override
900                     public AugmentationIdentifier load(final ImmutableSet<QName> key) {
901                         return new AugmentationIdentifier(key);
902                     }
903                 });
904
905         private final @NonNull ImmutableSet<QName> childNames;
906
907         @Override
908         public QName getNodeType() {
909             // This should rather throw exception than return always null
910             throw new UnsupportedOperationException("Augmentation node has no QName");
911         }
912
913         /**
914          * Construct new augmentation identifier using supplied set of possible
915          * child nodes.
916          *
917          * @param childNames
918          *            Set of possible child nodes.
919          */
920         public AugmentationIdentifier(final ImmutableSet<QName> childNames) {
921             this.childNames = requireNonNull(childNames);
922         }
923
924         /**
925          * Construct new augmentation identifier using supplied set of possible
926          * child nodes.
927          *
928          * @param childNames
929          *            Set of possible child nodes.
930          */
931         public AugmentationIdentifier(final Set<QName> childNames) {
932             this.childNames = ImmutableSet.copyOf(childNames);
933         }
934
935         /**
936          * Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
937          * uses a global instance cache, resulting in object reuse for equal inputs.
938          *
939          * @param childNames Set of possible child nodes
940          * @return An {@link AugmentationIdentifier}
941          */
942         public static @NonNull AugmentationIdentifier create(final ImmutableSet<QName> childNames) {
943             return CACHE.getUnchecked(childNames);
944         }
945
946         /**
947          * Return an AugmentationIdentifier for a particular set of QNames. Unlike the constructor, this factory method
948          * uses a global instance cache, resulting in object reuse for equal inputs.
949          *
950          * @param childNames Set of possible child nodes
951          * @return An {@link AugmentationIdentifier}
952          */
953         public static @NonNull AugmentationIdentifier create(final Set<QName> childNames) {
954             final AugmentationIdentifier existing = CACHE.getIfPresent(childNames);
955             return existing != null ? existing : create(ImmutableSet.copyOf(childNames));
956         }
957
958         /**
959          * Returns set of all possible child nodes.
960          *
961          * @return set of all possible child nodes.
962          */
963         public @NonNull Set<QName> getPossibleChildNames() {
964             return childNames;
965         }
966
967         @Override
968         public String toString() {
969             return "AugmentationIdentifier{" + "childNames=" + childNames + '}';
970         }
971
972         @Override
973         public String toRelativeString(final PathArgument previous) {
974             return toString();
975         }
976
977         @Override
978         public boolean equals(final Object obj) {
979             if (this == obj) {
980                 return true;
981             }
982             if (!(obj instanceof AugmentationIdentifier)) {
983                 return false;
984             }
985
986             AugmentationIdentifier that = (AugmentationIdentifier) obj;
987             return childNames.equals(that.childNames);
988         }
989
990         @Override
991         public int hashCode() {
992             return childNames.hashCode();
993         }
994
995         @Override
996         @SuppressWarnings("checkstyle:parameterName")
997         public int compareTo(final PathArgument o) {
998             if (!(o instanceof AugmentationIdentifier)) {
999                 return -1;
1000             }
1001             AugmentationIdentifier other = (AugmentationIdentifier) o;
1002             Set<QName> otherChildNames = other.getPossibleChildNames();
1003             int thisSize = childNames.size();
1004             int otherSize = otherChildNames.size();
1005             if (thisSize == otherSize) {
1006                 // Quick Set-based comparison
1007                 if (childNames.equals(otherChildNames)) {
1008                     return 0;
1009                 }
1010
1011                 // We already know the sets are not equal, but have equal size, hence the sets differ in their elements,
1012                 // but potentially share a common set of elements. The most consistent way of comparing them is using
1013                 // total ordering defined by QName's compareTo. Hence convert both sets to lists ordered
1014                 // by QName.compareTo() and decide on the first differing element.
1015                 final List<QName> diff = new ArrayList<>(Sets.symmetricDifference(childNames, otherChildNames));
1016                 verify(!diff.isEmpty(), "Augmentation identifiers %s and %s report no difference", this, o);
1017                 diff.sort(QName::compareTo);
1018                 return childNames.contains(diff.get(0)) ? -1 : 1;
1019             } else if (thisSize < otherSize) {
1020                 return 1;
1021             } else {
1022                 return -1;
1023             }
1024         }
1025
1026         private Object writeReplace() {
1027             return new AIv1(this);
1028         }
1029     }
1030
1031     /**
1032      * Fluent Builder of Instance Identifier instances.
1033      */
1034     public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
1035         /**
1036          * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
1037          *
1038          * @param arg A {@link PathArgument} to be added
1039          * @return this builder
1040          */
1041         @NonNull InstanceIdentifierBuilder node(PathArgument arg);
1042
1043         /**
1044          * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
1045          *
1046          * @param nodeType QName of {@link NodeIdentifier} which will be added
1047          * @return this builder
1048          */
1049         @NonNull InstanceIdentifierBuilder node(QName nodeType);
1050
1051         /**
1052          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
1053          * instance identifier.
1054          *
1055          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
1056          * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
1057          * @return this builder
1058          */
1059         @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
1060
1061         /**
1062          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
1063          *
1064          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
1065          * @param key QName of key which will be added
1066          * @param value value of key which will be added
1067          * @return this builder
1068          */
1069         @NonNull InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
1070
1071         /**
1072          * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
1073          *
1074          * @param args {@link PathArgument}s to be added
1075          * @return this builder
1076          * @throws NullPointerException if any of the arguments is null
1077          */
1078         @Beta
1079         @NonNull InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
1080
1081         /**
1082          * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
1083          *
1084          * @param args {@link PathArgument}s to be added
1085          * @return this builder
1086          * @throws NullPointerException if any of the arguments is null
1087          */
1088         @Beta
1089         default @NonNull InstanceIdentifierBuilder append(final PathArgument... args) {
1090             return append(Arrays.asList(args));
1091         }
1092
1093         /**
1094          * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
1095          *
1096          * @return {@link YangInstanceIdentifier}
1097          */
1098         @Override
1099         YangInstanceIdentifier build();
1100     }
1101 }