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