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