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