Cleanup use of Guava library
[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.ImmutableSet;
19 import com.google.common.collect.Iterables;
20 import com.google.common.collect.Sets;
21 import java.io.Serializable;
22 import java.lang.reflect.Array;
23 import java.util.ArrayList;
24 import java.util.Arrays;
25 import java.util.Collection;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Objects;
31 import java.util.Optional;
32 import java.util.Set;
33 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
34 import javax.annotation.Nonnull;
35 import javax.annotation.Nullable;
36 import org.opendaylight.yangtools.concepts.Builder;
37 import org.opendaylight.yangtools.concepts.Immutable;
38 import org.opendaylight.yangtools.concepts.Path;
39 import org.opendaylight.yangtools.util.HashCodeBuilder;
40 import org.opendaylight.yangtools.util.ImmutableOffsetMap;
41 import org.opendaylight.yangtools.util.SharedSingletonMap;
42 import org.opendaylight.yangtools.yang.common.QName;
43 import org.opendaylight.yangtools.yang.common.QNameModule;
44 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
45
46 /**
47  * Unique identifier of a particular node instance in the data tree.
48  *
49  * <p>
50  * Java representation of YANG Built-in type <code>instance-identifier</code>,
51  * which conceptually is XPath expression minimized to uniquely identify element
52  * in data tree which conforms to constraints maintained by YANG Model,
53  * effectively this makes Instance Identifier a path to element in data tree.
54  *
55  * <p>
56  * Constraints put in YANG specification on instance-identifier allowed it to be
57  * effectively represented in Java and it's evaluation does not require
58  * full-blown XPath processor.
59  *
60  * <p>
61  * <h3>Path Arguments</h3>
62  * Path to the node represented in instance identifier consists of
63  * {@link PathArgument} which carries necessary information to uniquely identify
64  * node on particular level in the subtree.
65  *
66  * <ul>
67  * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality
68  * <code>0..1</code> in particular subtree in data tree.</li>
69  * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item),
70  * which has cardinality <code>0..n</code>.</li>
71  * <li>{@link NodeWithValue} - Identifier of instance <code>leaf</code> node or
72  * <code>leaf-list</code> node.</li>
73  * <li>{@link AugmentationIdentifier} - Identifier of instance of
74  * <code>augmentation</code> node.</li>
75  * </ul>
76  *
77  * @see <a href="http://tools.ietf.org/html/rfc6020#section-9.13">RFC6020</a>
78  */
79 public abstract class YangInstanceIdentifier implements Path<YangInstanceIdentifier>, Immutable, Serializable {
80     /**
81      * An empty {@link YangInstanceIdentifier}. It corresponds to the path of the conceptual
82      * root of the YANG namespace.
83      */
84     public static final YangInstanceIdentifier EMPTY = FixedYangInstanceIdentifier.EMPTY_INSTANCE;
85
86     private static final AtomicReferenceFieldUpdater<YangInstanceIdentifier, String> TOSTRINGCACHE_UPDATER =
87             AtomicReferenceFieldUpdater.newUpdater(YangInstanceIdentifier.class, String.class, "toStringCache");
88     private static final long serialVersionUID = 4L;
89
90     private final int hash;
91     private transient volatile String toStringCache = null;
92
93     // Package-private to prevent outside subclassing
94     YangInstanceIdentifier(final int hash) {
95         this.hash = hash;
96     }
97
98     @Nonnull abstract YangInstanceIdentifier createRelativeIdentifier(int skipFromRoot);
99
100     @Nonnull abstract Collection<PathArgument> tryPathArguments();
101
102     @Nonnull abstract Collection<PathArgument> tryReversePathArguments();
103
104     /**
105      * Check if this instance identifier has empty path arguments, e.g. it is
106      * empty and corresponds to {@link #EMPTY}.
107      *
108      * @return True if this instance identifier is empty, false otherwise.
109      */
110     public abstract boolean isEmpty();
111
112     /**
113      * Return an optimized version of this identifier, useful when the identifier
114      * will be used very frequently.
115      *
116      * @return A optimized equivalent instance.
117      */
118     @Beta
119     public abstract YangInstanceIdentifier toOptimized();
120
121     /**
122      * Return the conceptual parent {@link YangInstanceIdentifier}, which has
123      * one item less in {@link #getPathArguments()}.
124      *
125      * @return Parent {@link YangInstanceIdentifier}, or null if this object is {@link #EMPTY}.
126      */
127     @Nullable public abstract YangInstanceIdentifier getParent();
128
129     /**
130      * Return the ancestor {@link YangInstanceIdentifier} with a particular depth, e.g. number of path arguments.
131      *
132      * @param depth Ancestor depth
133      * @return Ancestor {@link YangInstanceIdentifier}
134      * @throws IllegalArgumentException if the specified depth is negative or is greater than the depth of this object.
135      */
136     @Nonnull public abstract YangInstanceIdentifier getAncestor(int depth);
137
138     /**
139      * Returns an ordered iteration of path arguments.
140      *
141      * @return Immutable iteration of path arguments.
142      */
143     public abstract List<PathArgument> getPathArguments();
144
145     /**
146      * Returns an iterable of path arguments in reverse order. This is useful
147      * when walking up a tree organized this way.
148      *
149      * @return Immutable iterable of path arguments in reverse order.
150      */
151     public abstract List<PathArgument> getReversePathArguments();
152
153     /**
154      * Returns the last PathArgument. This is equivalent of iterating
155      * to the last element of the iterable returned by {@link #getPathArguments()}.
156      *
157      * @return The last past argument, or null if there are no PathArguments.
158      */
159     public abstract PathArgument getLastPathArgument();
160
161     public static YangInstanceIdentifier create(final Iterable<? extends PathArgument> path) {
162         if (Iterables.isEmpty(path)) {
163             return EMPTY;
164         }
165
166         final HashCodeBuilder<PathArgument> hash = new HashCodeBuilder<>();
167         for (PathArgument a : path) {
168             hash.addArgument(a);
169         }
170
171         return FixedYangInstanceIdentifier.create(path, hash.build());
172     }
173
174     public static YangInstanceIdentifier create(final PathArgument... path) {
175         // We are forcing a copy, since we cannot trust the user
176         return create(Arrays.asList(path));
177     }
178
179     boolean pathArgumentsEqual(final YangInstanceIdentifier other) {
180         return Iterables.elementsEqual(getPathArguments(), other.getPathArguments());
181     }
182
183     @Override
184     public boolean equals(final Object obj) {
185         if (this == obj) {
186             return true;
187         }
188         if (!(obj instanceof YangInstanceIdentifier)) {
189             return false;
190         }
191         YangInstanceIdentifier other = (YangInstanceIdentifier) obj;
192         if (this.hashCode() != obj.hashCode()) {
193             return false;
194         }
195
196         return pathArgumentsEqual(other);
197     }
198
199     /**
200      * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments.
201      *
202      * @param name QName of {@link NodeIdentifier}
203      * @return Instance Identifier with additional path argument added to the end.
204      */
205     public final YangInstanceIdentifier node(final QName name) {
206         return node(new NodeIdentifier(name));
207     }
208
209     /**
210      * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments.
211      *
212      * @param arg Path argument which should be added to the end
213      * @return Instance Identifier with additional path argument added to the end.
214      */
215     public final YangInstanceIdentifier node(final PathArgument arg) {
216         return new StackedYangInstanceIdentifier(this, arg, HashCodeBuilder.nextHashCode(hash, arg));
217     }
218
219     /**
220      * Get the relative path from an ancestor. This method attempts to perform
221      * the reverse of concatenating a base (ancestor) and a path.
222      *
223      * @param ancestor
224      *            Ancestor against which the relative path should be calculated
225      * @return This object's relative path from parent, or Optional.absent() if
226      *         the specified parent is not in fact an ancestor of this object.
227      */
228     public Optional<YangInstanceIdentifier> relativeTo(final YangInstanceIdentifier ancestor) {
229         if (this == ancestor) {
230             return Optional.of(EMPTY);
231         }
232         if (ancestor.isEmpty()) {
233             return Optional.of(this);
234         }
235
236         final Iterator<PathArgument> lit = getPathArguments().iterator();
237         final Iterator<PathArgument> oit = ancestor.getPathArguments().iterator();
238         int common = 0;
239
240         while (oit.hasNext()) {
241             // Ancestor is not really an ancestor
242             if (!lit.hasNext() || !lit.next().equals(oit.next())) {
243                 return Optional.empty();
244             }
245
246             ++common;
247         }
248
249         if (common == 0) {
250             return Optional.of(this);
251         }
252         if (!lit.hasNext()) {
253             return Optional.of(EMPTY);
254         }
255
256         return Optional.of(createRelativeIdentifier(common));
257     }
258
259     @Override
260     public final boolean contains(@Nonnull final YangInstanceIdentifier other) {
261         if (this == other) {
262             return true;
263         }
264
265         checkArgument(other != null, "other should not be null");
266         final Iterator<PathArgument> lit = getPathArguments().iterator();
267         final Iterator<PathArgument> oit = other.getPathArguments().iterator();
268
269         while (lit.hasNext()) {
270             if (!oit.hasNext()) {
271                 return false;
272             }
273
274             if (!lit.next().equals(oit.next())) {
275                 return false;
276             }
277         }
278
279         return true;
280     }
281
282     @Override
283     public final String toString() {
284         /*
285          * The toStringCache is safe, since the object contract requires
286          * immutability of the object and all objects referenced from this
287          * object.
288          * Used lists, maps are immutable. Path Arguments (elements) are also
289          * immutable, since the PathArgument contract requires immutability.
290          * The cache is thread-safe - if multiple computations occurs at the
291          * same time, cache will be overwritten with same result.
292          */
293         String ret = toStringCache;
294         if (ret == null) {
295             final StringBuilder builder = new StringBuilder("/");
296             PathArgument prev = null;
297             for (PathArgument argument : getPathArguments()) {
298                 if (prev != null) {
299                     builder.append('/');
300                 }
301                 builder.append(argument.toRelativeString(prev));
302                 prev = argument;
303             }
304
305             ret = builder.toString();
306             TOSTRINGCACHE_UPDATER.lazySet(this, ret);
307         }
308         return ret;
309     }
310
311     @Override
312     public final int hashCode() {
313         /*
314          * The caching is safe, since the object contract requires
315          * immutability of the object and all objects referenced from this
316          * object.
317          * Used lists, maps are immutable. Path Arguments (elements) are also
318          * immutable, since the PathArgument contract requires immutability.
319          */
320         return hash;
321     }
322
323     private static int hashCode(final Object value) {
324         if (value == null) {
325             return 0;
326         }
327
328         if (byte[].class.equals(value.getClass())) {
329             return Arrays.hashCode((byte[]) value);
330         }
331
332         if (value.getClass().isArray()) {
333             int hash = 0;
334             int length = Array.getLength(value);
335             for (int i = 0; i < length; i++) {
336                 hash += Objects.hashCode(Array.get(value, i));
337             }
338
339             return hash;
340         }
341
342         return Objects.hashCode(value);
343     }
344
345     // Static factories & helpers
346
347     /**
348      * Returns a new InstanceIdentifier with only one path argument of type {@link NodeIdentifier} with supplied
349      * QName.
350      *
351      * @param name QName of first node identifier
352      * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
353      */
354     public static YangInstanceIdentifier of(final QName name) {
355         return create(new NodeIdentifier(name));
356     }
357
358     /**
359      * Returns new builder for InstanceIdentifier with empty path arguments.
360      *
361      * @return new builder for InstanceIdentifier with empty path arguments.
362      */
363     public static InstanceIdentifierBuilder builder() {
364         return new YangInstanceIdentifierBuilder();
365     }
366
367     /**
368      * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
369      *
370      * @param origin InstanceIdentifier from which path arguments are copied.
371      * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
372      */
373     public static InstanceIdentifierBuilder builder(final YangInstanceIdentifier origin) {
374         return new YangInstanceIdentifierBuilder(origin.getPathArguments(), origin.hashCode());
375     }
376
377     /**
378      * Path argument / component of InstanceIdentifier.
379      * Path argument uniquely identifies node in data tree on particular
380      * level.
381      *
382      * <p>
383      * This interface itself is used as common parent for actual
384      * path arguments types and should not be implemented by user code.
385      *
386      * <p>
387      * Path arguments SHOULD contain only minimum of information
388      * required to uniquely identify node on particular subtree level.
389      *
390      * <p>
391      * For actual path arguments types see:
392      * <ul>
393      * <li>{@link NodeIdentifier} - Identifier of container or leaf
394      * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
395      * <li>{@link AugmentationIdentifier} - Identifier of augmentation
396      * <li>{@link NodeWithValue} - Identifier of leaf-list entry
397      * </ul>
398      */
399     public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
400         /**
401          * If applicable returns unique QName of data node as defined in YANG
402          * Schema.
403          *
404          * <p>
405          * This method may return null, if the corresponding schema node, does
406          * not have QName associated, such as in cases of augmentations.
407          *
408          * @return Node type
409          */
410         QName getNodeType();
411
412         /**
413          * Return the string representation of this object for use in context
414          * provided by a previous object. This method can be implemented in
415          * terms of {@link #toString()}, but implementations are encourage to
416          * reuse any context already emitted by the previous object.
417          *
418          * @param previous Previous path argument
419          * @return String representation
420          */
421         String toRelativeString(PathArgument previous);
422     }
423
424     private abstract static class AbstractPathArgument implements PathArgument {
425         private static final long serialVersionUID = -4546547994250849340L;
426         private final QName nodeType;
427         private transient int hashValue;
428         private transient volatile boolean hashGuard = false;
429
430         protected AbstractPathArgument(final QName nodeType) {
431             this.nodeType = requireNonNull(nodeType);
432         }
433
434         @Override
435         public final QName getNodeType() {
436             return nodeType;
437         }
438
439         @Override
440         @SuppressWarnings("checkstyle:parameterName")
441         public int compareTo(@Nonnull final PathArgument o) {
442             return nodeType.compareTo(o.getNodeType());
443         }
444
445         protected int hashCodeImpl() {
446             return 31 + getNodeType().hashCode();
447         }
448
449         @Override
450         public final int hashCode() {
451             if (!hashGuard) {
452                 hashValue = hashCodeImpl();
453                 hashGuard = true;
454             }
455
456             return hashValue;
457         }
458
459         @Override
460         public boolean equals(final Object obj) {
461             if (this == obj) {
462                 return true;
463             }
464             if (obj == null || this.getClass() != obj.getClass()) {
465                 return false;
466             }
467
468             return getNodeType().equals(((AbstractPathArgument)obj).getNodeType());
469         }
470
471         @Override
472         public String toString() {
473             return getNodeType().toString();
474         }
475
476         @Override
477         public String toRelativeString(final PathArgument previous) {
478             if (previous instanceof AbstractPathArgument) {
479                 final QNameModule mod = previous.getNodeType().getModule();
480                 if (getNodeType().getModule().equals(mod)) {
481                     return getNodeType().getLocalName();
482                 }
483             }
484
485             return getNodeType().toString();
486         }
487     }
488
489     /**
490      * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
491      * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
492      */
493     public static final class NodeIdentifier extends AbstractPathArgument {
494         private static final long serialVersionUID = -2255888212390871347L;
495         private static final LoadingCache<QName, NodeIdentifier> CACHE = CacheBuilder.newBuilder().weakValues()
496                 .build(new CacheLoader<QName, NodeIdentifier>() {
497                     @Override
498                     public NodeIdentifier load(@Nonnull final QName key) {
499                         return new NodeIdentifier(key);
500                     }
501                 });
502
503         public NodeIdentifier(final QName node) {
504             super(node);
505         }
506
507         /**
508          * Return a NodeIdentifier for a particular QName. Unlike the constructor, this factory method uses a global
509          * instance cache, resulting in object reuse for equal inputs.
510          *
511          * @param node Node's QName
512          * @return A {@link NodeIdentifier}
513          */
514         public static NodeIdentifier create(final QName node) {
515             return CACHE.getUnchecked(node);
516         }
517     }
518
519     /**
520      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
521      * overall data tree.
522      */
523     public static final class NodeIdentifierWithPredicates extends AbstractPathArgument {
524         private static final long serialVersionUID = -4787195606494761540L;
525
526         private final Map<QName, Object> keyValues;
527
528         public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
529             super(node);
530             // Retains ImmutableMap for empty maps. For larger sizes uses a shared key set.
531             this.keyValues = ImmutableOffsetMap.unorderedCopyOf(keyValues);
532         }
533
534         public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
535             super(node);
536             this.keyValues = SharedSingletonMap.unorderedOf(key, value);
537         }
538
539         public Map<QName, Object> getKeyValues() {
540             return keyValues;
541         }
542
543         @Override
544         protected int hashCodeImpl() {
545             final int prime = 31;
546             int result = super.hashCodeImpl();
547             result = prime * result;
548
549             for (Entry<QName, Object> entry : keyValues.entrySet()) {
550                 result += Objects.hashCode(entry.getKey()) + YangInstanceIdentifier.hashCode(entry.getValue());
551             }
552             return result;
553         }
554
555         @Override
556         @SuppressWarnings("checkstyle:equalsHashCode")
557         public boolean equals(final Object obj) {
558             if (!super.equals(obj)) {
559                 return false;
560             }
561
562             final Map<QName, Object> otherKeyValues = ((NodeIdentifierWithPredicates) obj).keyValues;
563
564             // TODO: benchmark to see if just calling equals() on the two maps is not faster
565             if (keyValues == otherKeyValues) {
566                 return true;
567             }
568             if (keyValues.size() != otherKeyValues.size()) {
569                 return false;
570             }
571
572             for (Entry<QName, Object> entry : keyValues.entrySet()) {
573                 if (!otherKeyValues.containsKey(entry.getKey())
574                         || !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) {
575
576                     return false;
577                 }
578             }
579
580             return true;
581         }
582
583         @Override
584         public String toString() {
585             return super.toString() + '[' + keyValues + ']';
586         }
587
588         @Override
589         public String toRelativeString(final PathArgument previous) {
590             return super.toRelativeString(previous) + '[' + keyValues + ']';
591         }
592     }
593
594     /**
595      * Simple path argument identifying a {@link LeafSetEntryNode} leaf
596      * overall data tree.
597      */
598     public static final class NodeWithValue<T> extends AbstractPathArgument {
599         private static final long serialVersionUID = -3637456085341738431L;
600
601         private final T value;
602
603         public NodeWithValue(final QName node, final T value) {
604             super(node);
605             this.value = value;
606         }
607
608         public T getValue() {
609             return value;
610         }
611
612         @Override
613         protected int hashCodeImpl() {
614             final int prime = 31;
615             int result = super.hashCodeImpl();
616             result = prime * result + YangInstanceIdentifier.hashCode(value);
617             return result;
618         }
619
620         @Override
621         @SuppressWarnings("checkstyle:equalsHashCode")
622         public boolean equals(final Object obj) {
623             if (!super.equals(obj)) {
624                 return false;
625             }
626             final NodeWithValue<?> other = (NodeWithValue<?>) obj;
627             return Objects.deepEquals(value, other.value);
628         }
629
630         @Override
631         public String toString() {
632             return super.toString() + '[' + value + ']';
633         }
634
635         @Override
636         public String toRelativeString(final PathArgument previous) {
637             return super.toRelativeString(previous) + '[' + value + ']';
638         }
639     }
640
641     /**
642      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode}
643      * node in particular subtree.
644      *
645      * <p>
646      * Augmentation is uniquely identified by set of all possible child nodes.
647      * This is possible
648      * to identify instance of augmentation,
649      * since RFC6020 states that <code>augment</code> that augment
650      * statement must not add multiple nodes from same namespace
651      * / module to the target node.
652      *
653      * @see <a href="http://tools.ietf.org/html/rfc6020#section-7.15">RFC6020</a>
654      */
655     public static final class AugmentationIdentifier implements PathArgument {
656         private static final long serialVersionUID = -8122335594681936939L;
657         private final ImmutableSet<QName> childNames;
658
659         @Override
660         public QName getNodeType() {
661             // This should rather throw exception than return always null
662             throw new UnsupportedOperationException("Augmentation node has no QName");
663         }
664
665         /**
666          * Construct new augmentation identifier using supplied set of possible
667          * child nodes.
668          *
669          * @param childNames
670          *            Set of possible child nodes.
671          */
672         public AugmentationIdentifier(final Set<QName> childNames) {
673             this.childNames = ImmutableSet.copyOf(childNames);
674         }
675
676         /**
677          * Returns set of all possible child nodes.
678          *
679          * @return set of all possible child nodes.
680          */
681         public Set<QName> getPossibleChildNames() {
682             return childNames;
683         }
684
685         @Override
686         public String toString() {
687             return "AugmentationIdentifier{" + "childNames=" + childNames + '}';
688         }
689
690         @Override
691         public String toRelativeString(final PathArgument previous) {
692             return toString();
693         }
694
695         @Override
696         public boolean equals(final Object obj) {
697             if (this == obj) {
698                 return true;
699             }
700             if (!(obj instanceof AugmentationIdentifier)) {
701                 return false;
702             }
703
704             AugmentationIdentifier that = (AugmentationIdentifier) obj;
705             return childNames.equals(that.childNames);
706         }
707
708         @Override
709         public int hashCode() {
710             return childNames.hashCode();
711         }
712
713         @Override
714         @SuppressWarnings("checkstyle:parameterName")
715         public int compareTo(@Nonnull final PathArgument o) {
716             if (!(o instanceof AugmentationIdentifier)) {
717                 return -1;
718             }
719             AugmentationIdentifier other = (AugmentationIdentifier) o;
720             Set<QName> otherChildNames = other.getPossibleChildNames();
721             int thisSize = childNames.size();
722             int otherSize = otherChildNames.size();
723             if (thisSize == otherSize) {
724                 // Quick Set-based comparison
725                 if (childNames.equals(otherChildNames)) {
726                     return 0;
727                 }
728
729                 // We already know the sets are not equal, but have equal size, hence the sets differ in their elements,
730                 // but potentially share a common set of elements. The most consistent way of comparing them is using
731                 // total ordering defined by QName's compareTo. Hence convert both sets to lists ordered
732                 // by QName.compareTo() and decide on the first differing element.
733                 final List<QName> diff = new ArrayList<>(Sets.symmetricDifference(childNames, otherChildNames));
734                 verify(!diff.isEmpty(), "Augmentation identifiers %s and %s report no difference", this, o);
735                 diff.sort(QName::compareTo);
736                 return childNames.contains(diff.get(0)) ? -1 : 1;
737             } else if (thisSize < otherSize) {
738                 return 1;
739             } else {
740                 return -1;
741             }
742         }
743     }
744
745     /**
746      * Fluent Builder of Instance Identifier instances.
747      */
748     public interface InstanceIdentifierBuilder extends Builder<YangInstanceIdentifier> {
749         /**
750          * Adds a {@link PathArgument} to path arguments of resulting instance identifier.
751          *
752          * @param arg A {@link PathArgument} to be added
753          * @return this builder
754          */
755         InstanceIdentifierBuilder node(PathArgument arg);
756
757         /**
758          * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
759          *
760          * @param nodeType QName of {@link NodeIdentifier} which will be added
761          * @return this builder
762          */
763         InstanceIdentifierBuilder node(QName nodeType);
764
765         /**
766          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting
767          * instance identifier.
768          *
769          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
770          * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
771          * @return this builder
772          */
773         InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
774
775         /**
776          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
777          *
778          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
779          * @param key QName of key which will be added
780          * @param value value of key which will be added
781          * @return this builder
782          */
783         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
784
785         /**
786          * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
787          *
788          * @param args {@link PathArgument}s to be added
789          * @return this builder
790          * @throws NullPointerException if any of the arguments is null
791          */
792         @Beta
793         InstanceIdentifierBuilder append(Collection<? extends PathArgument> args);
794
795         /**
796          * Adds a collection of {@link PathArgument}s to path arguments of resulting instance identifier.
797          *
798          * @param args {@link PathArgument}s to be added
799          * @return this builder
800          * @throws NullPointerException if any of the arguments is null
801          */
802         @Beta
803         default InstanceIdentifierBuilder append(final PathArgument... args) {
804             return append(Arrays.asList(args));
805         }
806
807         /**
808          * Builds an {@link YangInstanceIdentifier} with path arguments from this builder.
809          *
810          * @return {@link YangInstanceIdentifier}
811          */
812         @Override
813         YangInstanceIdentifier build();
814     }
815 }