24439e2c8c82b0244d9d43d164f2843392b39ead
[mdsal.git] / binding / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / InstanceIdentifier.java
1 /*
2  * Copyright (c) 2013 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.binding;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.MoreObjects;
14 import com.google.common.base.MoreObjects.ToStringHelper;
15 import com.google.common.collect.ImmutableCollection;
16 import com.google.common.collect.ImmutableList;
17 import com.google.common.collect.Iterables;
18 import java.io.IOException;
19 import java.io.Serializable;
20 import java.lang.reflect.Field;
21 import java.util.ArrayList;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.List;
25 import org.opendaylight.yangtools.concepts.Builder;
26 import org.opendaylight.yangtools.concepts.Immutable;
27 import org.opendaylight.yangtools.concepts.Path;
28 import org.opendaylight.yangtools.util.HashCodeBuilder;
29
30 /**
31  * This instance identifier uniquely identifies a specific DataObject in the data tree modeled by YANG.
32  *
33  * <p>
34  * For Example let's say you were trying to refer to a node in inventory which was modeled in YANG as follows,
35  *
36  * <p>
37  * <pre>
38  * module opendaylight-inventory {
39  *      ....
40  *
41  *      container nodes {
42  *        list node {
43  *            key "id";
44  *            ext:context-instance "node-context";
45  *
46  *            uses node;
47  *        }
48  *    }
49  *
50  * }
51  * </pre>
52  *
53  * <p>
54  * You can create an instance identifier as follows to get to a node with id "openflow:1": {@code
55  * InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build();
56  * }
57  *
58  * <p>
59  * This would be the same as using a path like so, "/nodes/node/openflow:1" to refer to the openflow:1 node
60  */
61 public class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>,
62         Immutable, Serializable {
63     private static final Field PATHARGUMENTS_FIELD;
64     private static final long serialVersionUID = 2L;
65     /*
66      * Protected to differentiate internal and external access. Internal
67      * access is required never to modify the contents. References passed
68      * to outside entities have to be wrapped in an unmodifiable view.
69      */
70     protected final transient Iterable<PathArgument> pathArguments;
71     private final Class<T> targetType;
72     private final boolean wildcarded;
73     private final int hash;
74
75     static {
76         final Field f;
77         try {
78             f = InstanceIdentifier.class.getDeclaredField("pathArguments");
79         } catch (NoSuchFieldException | SecurityException e) {
80             throw new ExceptionInInitializerError(e);
81         }
82         f.setAccessible(true);
83         PATHARGUMENTS_FIELD = f;
84     }
85
86     InstanceIdentifier(final Class<T> type, final Iterable<PathArgument> pathArguments, final boolean wildcarded,
87             final int hash) {
88         this.pathArguments = requireNonNull(pathArguments);
89         this.targetType = requireNonNull(type);
90         this.wildcarded = wildcarded;
91         this.hash = hash;
92     }
93
94     /**
95      * Return the type of data which this InstanceIdentifier identifies.
96      *
97      * @return Target type
98      */
99     public final Class<T> getTargetType() {
100         return targetType;
101     }
102
103     /**
104      * Return the path argument chain which makes up this instance identifier.
105      *
106      * @return Path argument chain. Immutable and does not contain nulls.
107      */
108     public final Iterable<PathArgument> getPathArguments() {
109         return Iterables.unmodifiableIterable(pathArguments);
110     }
111
112     /**
113      * Check whether an instance identifier contains any wildcards. A wildcard is an path argument which has a null key.
114      *
115      * @return true if any of the path arguments has a null key.
116      */
117     public final boolean isWildcarded() {
118         return wildcarded;
119     }
120
121     @Override
122     public final int hashCode() {
123         return hash;
124     }
125
126     @Override
127     public final boolean equals(final Object obj) {
128         if (this == obj) {
129             return true;
130         }
131         if (obj == null) {
132             return false;
133         }
134         if (getClass() != obj.getClass()) {
135             return false;
136         }
137
138         final InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
139         if (pathArguments == other.pathArguments) {
140             return true;
141         }
142
143         /*
144          * We could now just go and compare the pathArguments, but that
145          * can be potentially expensive. Let's try to avoid that by
146          * checking various things that we have cached from pathArguments
147          * and trying to prove the identifiers are *not* equal.
148          */
149         if (hash != other.hash) {
150             return false;
151         }
152         if (wildcarded != other.wildcarded) {
153             return false;
154         }
155         if (targetType != other.targetType) {
156             return false;
157         }
158         if (fastNonEqual(other)) {
159             return false;
160         }
161
162         // Everything checks out so far, so we have to do a full equals
163         return Iterables.elementsEqual(pathArguments, other.pathArguments);
164     }
165
166     /**
167      * Perform class-specific fast checks for non-equality. This allows subclasses to avoid iterating over the
168      * pathArguments by performing quick checks on their specific fields.
169      *
170      * @param other The other identifier, guaranteed to be the same class
171      * @return true if the other identifier cannot be equal to this one.
172      */
173     protected boolean fastNonEqual(final InstanceIdentifier<?> other) {
174         return false;
175     }
176
177     @Override
178     public final String toString() {
179         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
180     }
181
182     /**
183      * Add class-specific toString attributes.
184      *
185      * @param toStringHelper ToStringHelper instance
186      * @return ToStringHelper instance which was passed in
187      */
188     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
189         return toStringHelper.add("targetType", targetType).add("path", Iterables.toString(pathArguments));
190     }
191
192     /**
193      * Return an instance identifier trimmed at the first occurrence of a specific component type.
194      *
195      * <p>
196      * For example let's say an instance identifier was built like so,
197      * <pre>
198      *      identifier = InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class,
199      *                   new NodeKey(new NodeId("openflow:1")).build();
200      * </pre>
201      *
202      * <p>
203      * And you wanted to obtain the Instance identifier which represented Nodes you would do it like so,
204      *
205      * <p>
206      * <pre>
207      *      identifier.firstIdentifierOf(Nodes.class)
208      * </pre>
209      *
210      * @param type component type
211      * @return trimmed instance identifier, or null if the component type
212      *         is not present.
213      */
214     public final <I extends DataObject> InstanceIdentifier<I> firstIdentifierOf(final Class<I> type) {
215         int count = 1;
216         for (final PathArgument a : pathArguments) {
217             if (type.equals(a.getType())) {
218                 @SuppressWarnings("unchecked")
219                 final InstanceIdentifier<I> ret = (InstanceIdentifier<I>) internalCreate(
220                         Iterables.limit(pathArguments, count));
221                 return ret;
222             }
223
224             ++count;
225         }
226
227         return null;
228     }
229
230     /**
231      * Return the key associated with the first component of specified type in
232      * an identifier.
233      *
234      * @param listItem component type
235      * @param listKey component key type
236      * @return key associated with the component, or null if the component type
237      *         is not present.
238      *
239      * @deprecated Use {@link #firstKeyOf(Class)} instead.
240      */
241     @Deprecated
242     public final <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem,
243             final Class<K> listKey) {
244         return firstKeyOf(listItem);
245     }
246
247     /**
248      * Return the key associated with the first component of specified type in
249      * an identifier.
250      *
251      * @param listItem component type
252      * @return key associated with the component, or null if the component type
253      *         is not present.
254      */
255     public final <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(
256             final Class<N> listItem) {
257         for (final PathArgument i : pathArguments) {
258             if (listItem.equals(i.getType())) {
259                 @SuppressWarnings("unchecked")
260                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
261                 return ret;
262             }
263         }
264
265         return null;
266     }
267
268     /**
269      * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all
270      * PathArguments to match exactly.
271      *
272      * <p>
273      * The contains method checks if the other identifier is fully contained within the current identifier. It does this
274      * by looking at only the types of the path arguments and not by comparing the path arguments themselves.
275      *
276      * <p>
277      * To illustrate here is an example which explains the working of this API. Let's say you have two instance
278      * identifiers as follows:
279      * {@code
280      * this = /nodes/node/openflow:1
281      * other = /nodes/node/openflow:2
282      * }
283      * then this.contains(other) will return false.
284      *
285      * @param other Potentially-container instance identifier
286      * @return True if the specified identifier is contained in this identifier.
287      */
288     @Override
289     public final boolean contains(final InstanceIdentifier<? extends DataObject> other) {
290         requireNonNull(other, "other should not be null");
291
292         final Iterator<?> lit = pathArguments.iterator();
293         final Iterator<?> oit = other.pathArguments.iterator();
294
295         while (lit.hasNext()) {
296             if (!oit.hasNext()) {
297                 return false;
298             }
299
300             if (!lit.next().equals(oit.next())) {
301                 return false;
302             }
303         }
304
305         return true;
306     }
307
308     /**
309      * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar
310      * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their
311      * non-wildcarded PathArgument counterpart.
312      *
313      * @param other Identifier which should be checked for inclusion.
314      * @return true if this identifier contains the other object
315      */
316     public final boolean containsWildcarded(final InstanceIdentifier<?> other) {
317         requireNonNull(other, "other should not be null");
318
319         final Iterator<PathArgument> lit = pathArguments.iterator();
320         final Iterator<PathArgument> oit = other.pathArguments.iterator();
321
322         while (lit.hasNext()) {
323             if (!oit.hasNext()) {
324                 return false;
325             }
326
327             final PathArgument la = lit.next();
328             final PathArgument oa = oit.next();
329
330             if (!la.getType().equals(oa.getType())) {
331                 return false;
332             }
333             if (la instanceof IdentifiableItem<?, ?> && oa instanceof IdentifiableItem<?, ?> && !la.equals(oa)) {
334                 return false;
335             }
336         }
337
338         return true;
339     }
340
341     private <N extends DataObject> InstanceIdentifier<N> childIdentifier(final AbstractPathArgument<N> arg) {
342         return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)),
343             HashCodeBuilder.nextHashCode(hash, arg), isWildcarded());
344     }
345
346     /**
347      * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to
348      * {@code builder().child(container).build()}.
349      *
350      * @param container Container to append
351      * @param <N> Container type
352      * @return An InstanceIdentifier.
353      * @throws NullPointerException if {@code container} is null
354      */
355     public final <N extends ChildOf<? super T>> InstanceIdentifier<N> child(final Class<N> container) {
356         return childIdentifier(new Item<>(container));
357     }
358
359     /**
360      * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to
361      * {@code builder().child(listItem, listKey).build()}.
362      *
363      * @param listItem List to append
364      * @param listKey List key
365      * @param <N> List type
366      * @param <K> Key type
367      * @return An InstanceIdentifier.
368      * @throws NullPointerException if any argument is null
369      */
370     @SuppressWarnings("unchecked")
371     public final <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> KeyedInstanceIdentifier<N, K>
372             child(final Class<N> listItem, final K listKey) {
373         return (KeyedInstanceIdentifier<N, K>) childIdentifier(new IdentifiableItem<>(listItem, listKey));
374     }
375
376     /**
377      * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to
378      * {@code builder().augmentation(container).build()}.
379      *
380      * @param container Container to append
381      * @param <N> Container type
382      * @return An InstanceIdentifier.
383      * @throws NullPointerException if {@code container} is null
384      */
385     public final <N extends DataObject & Augmentation<? super T>> InstanceIdentifier<N> augmentation(
386             final Class<N> container) {
387         return childIdentifier(new Item<>(container));
388     }
389
390     @Deprecated
391     private List<PathArgument> legacyCache;
392
393     /**
394      * Return the path as a list.
395      *
396      * @deprecated Use {@link #getPathArguments()} instead.
397      */
398     @Deprecated
399     public final List<PathArgument> getPath() {
400         if (legacyCache == null) {
401             legacyCache = ImmutableList.<PathArgument>copyOf(pathArguments);
402         }
403
404         return legacyCache;
405     }
406
407     /**
408      * Create a builder rooted at this key.
409      *
410      * @return A builder instance
411      */
412     public InstanceIdentifierBuilder<T> builder() {
413         return new InstanceIdentifierBuilderImpl<>(new Item<>(targetType), pathArguments, hash, isWildcarded());
414     }
415
416     /**
417      * Create a new InstanceIdentifierBuilder given a base InstanceIdentifier.
418      *
419      * @deprecated Use {@link #builder()} instead.
420      */
421     @Deprecated
422     public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(final InstanceIdentifier<T> base) {
423         return base.builder();
424     }
425
426     /**
427      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container.
428      *
429      * @param container Base container
430      * @param <T> Type of the container
431      * @return A new {@link InstanceIdentifierBuilder}
432      * @throws NullPointerException if {@code container} is null
433      */
434     public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(
435             final Class<T> container) {
436         return new InstanceIdentifierBuilderImpl<T>().addNode(container);
437     }
438
439     /**
440      * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an
441      * {@link IdentifiableItem}.
442      *
443      * @param listItem list item class
444      * @param listKey key value
445      * @param <N> List type
446      * @param <K> List key
447      * @return A new {@link InstanceIdentifierBuilder}
448      * @throws NullPointerException if any argument is null
449      */
450     public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>,
451             K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(final Class<N> listItem, final K listKey) {
452         return new InstanceIdentifierBuilderImpl<N>().addNode(listItem, listKey);
453     }
454
455     /**
456      * Create an instance identifier for a very specific object type. This method implements {@link #create(Iterable)}
457      * semantics, except it is used by internal callers, which have assured that the argument is an immutable Iterable.
458      *
459      * @param pathArguments The path to a specific node in the data tree
460      * @return InstanceIdentifier instance
461      * @throws IllegalArgumentException if pathArguments is empty or contains a null element.
462      * @throws NullPointerException if {@code pathArguments} is null
463      */
464     private static InstanceIdentifier<?> internalCreate(final Iterable<PathArgument> pathArguments) {
465         final Iterator<? extends PathArgument> it = requireNonNull(pathArguments, "pathArguments may not be null")
466                 .iterator();
467         final HashCodeBuilder<PathArgument> hashBuilder = new HashCodeBuilder<>();
468         boolean wildcard = false;
469         PathArgument arg = null;
470
471         while (it.hasNext()) {
472             arg = it.next();
473             checkArgument(arg != null, "pathArguments may not contain null elements");
474
475             // TODO: sanity check ChildOf<>;
476             hashBuilder.addArgument(arg);
477
478             if (Identifiable.class.isAssignableFrom(arg.getType()) && !(arg instanceof IdentifiableItem<?, ?>)) {
479                 wildcard = true;
480             }
481         }
482         checkArgument(arg != null, "pathArguments may not be empty");
483
484         return trustedCreate(arg, pathArguments, hashBuilder.build(), wildcard);
485     }
486
487     /**
488      * Create an instance identifier for a very specific object type.
489      *
490      * <p>
491      * Example:
492      * <pre>
493      *  List&lt;PathArgument&gt; path = Arrays.asList(new Item(Nodes.class))
494      *  new InstanceIdentifier(path);
495      * </pre>
496      *
497      * @param pathArguments The path to a specific node in the data tree
498      * @return InstanceIdentifier instance
499      * @throws IllegalArgumentException if pathArguments is empty or
500      *         contains a null element.
501      */
502     public static InstanceIdentifier<?> create(final Iterable<? extends PathArgument> pathArguments) {
503         if (pathArguments instanceof ImmutableCollection<?>) {
504             @SuppressWarnings("unchecked")
505             final Iterable<PathArgument> immutableArguments = (Iterable<PathArgument>) pathArguments;
506             return internalCreate(immutableArguments);
507         }
508
509         return internalCreate(ImmutableList.copyOf(pathArguments));
510     }
511
512     /**
513      * Create an instance identifier for a very specific object type.
514      *
515      * <p>
516      * For example
517      * <pre>
518      *      new InstanceIdentifier(Nodes.class)
519      * </pre>
520      * would create an InstanceIdentifier for an object of type Nodes
521      *
522      * @param type The type of the object which this instance identifier represents
523      * @return InstanceIdentifier instance
524      */
525     @SuppressWarnings("unchecked")
526     public static <T extends DataObject> InstanceIdentifier<T> create(final Class<T> type) {
527         return (InstanceIdentifier<T>) create(Collections.singletonList(new Item<>(type)));
528     }
529
530     /**
531      * Return the key associated with the last component of the specified identifier.
532      *
533      * @param id instance identifier
534      * @return key associated with the last component
535      * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
536      * @throws NullPointerException if id is null.
537      */
538     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(
539             final InstanceIdentifier<N> id) {
540         requireNonNull(id);
541         checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
542
543         @SuppressWarnings("unchecked")
544         final K ret = ((KeyedInstanceIdentifier<N, K>)id).getKey();
545         return ret;
546     }
547
548     @SuppressWarnings({ "unchecked", "rawtypes" })
549     static <N extends DataObject> InstanceIdentifier<N> trustedCreate(final PathArgument arg,
550             final Iterable<PathArgument> pathArguments, final int hash, boolean wildcarded) {
551         if (Identifiable.class.isAssignableFrom(arg.getType()) && !wildcarded) {
552             Identifier<?> key = null;
553             if (arg instanceof IdentifiableItem<?, ?>) {
554                 key = ((IdentifiableItem<?, ?>)arg).key;
555             } else {
556                 wildcarded = true;
557             }
558
559             return new KeyedInstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash, key);
560         }
561
562         return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash);
563     }
564
565     /**
566      * Path argument of {@link InstanceIdentifier}. Interface which implementations are used as path components of the
567      * path in overall data tree.
568      */
569     public interface PathArgument extends Comparable<PathArgument> {
570         /**
571          * Return the data object type backing this PathArgument.
572          *
573          * @return Data object type.
574          */
575         Class<? extends DataObject> getType();
576     }
577
578     private abstract static class AbstractPathArgument<T extends DataObject> implements PathArgument, Serializable {
579         private static final long serialVersionUID = 1L;
580         private final Class<T> type;
581
582         protected AbstractPathArgument(final Class<T> type) {
583             this.type = requireNonNull(type, "Type may not be null.");
584         }
585
586         @Override
587         public final Class<T> getType() {
588             return type;
589         }
590
591         @Override
592         public int hashCode() {
593             return type.hashCode();
594         }
595
596         @Override
597         public boolean equals(final Object obj) {
598             if (this == obj) {
599                 return true;
600             }
601             if (obj == null) {
602                 return false;
603             }
604             if (getClass() != obj.getClass()) {
605                 return false;
606             }
607             final AbstractPathArgument<?> other = (AbstractPathArgument<?>) obj;
608             return type.equals(other.type);
609         }
610
611         @Override
612         public int compareTo(final PathArgument arg) {
613             return type.getCanonicalName().compareTo(arg.getType().getCanonicalName());
614         }
615     }
616
617     /**
618      * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
619      * a kind. In YANG terms this would probably represent a container.
620      *
621      * @param <T> Item type
622      */
623     public static final class Item<T extends DataObject> extends AbstractPathArgument<T> {
624         private static final long serialVersionUID = 1L;
625
626         public Item(final Class<T> type) {
627             super(type);
628         }
629
630         @Override
631         public String toString() {
632             return getType().getName();
633         }
634     }
635
636     /**
637      * An IdentifiableItem represents a object that is usually present in a collection and can be identified uniquely
638      * by a key. In YANG terms this would probably represent an item in a list.
639      *
640      * @param <I> An object that is identifiable by an identifier
641      * @param <T> The identifier of the object
642      */
643     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>>
644             extends AbstractPathArgument<I> {
645         private static final long serialVersionUID = 1L;
646         private final T key;
647
648         public IdentifiableItem(final Class<I> type, final T key) {
649             super(type);
650             this.key = requireNonNull(key, "Key may not be null.");
651         }
652
653         /**
654          * Return the data object type backing this PathArgument.
655          *
656          * @return Data object type.
657          */
658         public T getKey() {
659             return this.key;
660         }
661
662         @Override
663         public boolean equals(final Object obj) {
664             return super.equals(obj) && key.equals(((IdentifiableItem<?, ?>) obj).getKey());
665         }
666
667         @Override
668         public int hashCode() {
669             return super.hashCode() * 31 + key.hashCode();
670         }
671
672         @Override
673         public String toString() {
674             return getType().getName() + "[key=" + key + "]";
675         }
676     }
677
678     public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
679         /**
680          * Append the specified container as a child of the current InstanceIdentifier referenced by the builder.
681          *
682          * This method should be used when you want to build an instance identifier by appending top-level
683          * elements
684          *
685          * Example,
686          * <pre>
687          *     InstanceIdentifier.builder().child(Nodes.class).build();
688          * </pre>
689          *
690          * NOTE :- The above example is only for illustration purposes InstanceIdentifier.builder() has been deprecated
691          * and should not be used. Use InstanceIdentifier.builder(Nodes.class) instead
692          *
693          * @param container Container to append
694          * @param <N> Container type
695          * @return this builder
696          * @throws NullPointerException if {@code container} is null
697          */
698         <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
699
700         /**
701          * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder.
702          *
703          * This method should be used when you want to build an instance identifier by appending a specific list element
704          * to the identifier
705          *
706          * @param listItem List to append
707          * @param listKey List key
708          * @param <N> List type
709          * @param <K> Key type
710          * @return this builder
711          * @throws NullPointerException if any argument is null
712          */
713         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
714                 Class<N> listItem, K listKey);
715
716         /**
717          * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by
718          * the builder.
719          *
720          * @param container augmentation class
721          * @param <N> augmentation type
722          * @return this builder
723          * @throws NullPointerException if {@code container} is null
724          */
725         <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
726
727         /**
728          * Build the instance identifier.
729          *
730          * @return Resulting instance identifier.
731          */
732         @Override
733         InstanceIdentifier<T> build();
734     }
735
736     private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
737         out.defaultWriteObject();
738         out.writeInt(Iterables.size(pathArguments));
739         for (Object o : pathArguments) {
740             out.writeObject(o);
741         }
742     }
743
744     private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
745         in.defaultReadObject();
746
747         final int size = in.readInt();
748         final List<PathArgument> args = new ArrayList<>(size);
749         for (int i = 0; i < size; ++i) {
750             args.add((PathArgument) in.readObject());
751         }
752
753         try {
754             PATHARGUMENTS_FIELD.set(this, ImmutableList.copyOf(args));
755         } catch (IllegalArgumentException | IllegalAccessException e) {
756             throw new IOException(e);
757         }
758     }
759 }