Added utility functions which allows for resolving InstanceIdentifier against concret...
[yangtools.git] / yang / 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 java.util.Collections;
11 import java.util.List;
12
13 import org.opendaylight.yangtools.concepts.Builder;
14 import org.opendaylight.yangtools.concepts.Immutable;
15 import org.opendaylight.yangtools.concepts.Path;
16
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.Iterables;
19
20 /**
21  * Uniquely identifies data location in the overall of data tree
22  * modeled by YANG.
23  *
24  *
25  */
26 public final class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>,Immutable {
27
28     private final List<PathArgument> path;
29     private final Class<T> targetType;
30
31     public InstanceIdentifier(Class<T> type) {
32         path = Collections.<PathArgument> singletonList(new Item<>(type));
33         this.targetType = type;
34     }
35
36     public InstanceIdentifier(List<PathArgument> path, Class<T> type) {
37         this.path = ImmutableList.copyOf(path);
38         this.targetType = type;
39     }
40
41     /**
42      *
43      * @return path
44      */
45     public List<PathArgument> getPath() {
46         return getPathArguments();
47     }
48
49     public List<PathArgument> getPathArguments() {
50         return this.path;
51     }
52
53     public Class<T> getTargetType() {
54         return this.targetType;
55     }
56
57     @Override
58     public String toString() {
59         return "InstanceIdentifier [path=" + path + "]";
60     }
61
62     /**
63      * Return an instance identifier trimmed at the first occurrence of a
64      * specific component type.
65      *
66      * @param type component type
67      * @return trimmed instance identifier, or null if the component type
68      *         is not present.
69      */
70     @SuppressWarnings("hiding")
71     public <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final Class<T> type) {
72         int i = 1;
73         for (final PathArgument a : path) {
74             if (type.equals(a.getType())) {
75                 return new InstanceIdentifier<>(path.subList(0, i), type);
76             }
77
78             ++i;
79         }
80
81         return null;
82     }
83
84     /**
85      * Return the key associated with the first component of specified type in
86      * an identifier.
87      *
88      * @param listItem component type
89      * @param listKey component key type
90      * @return key associated with the component, or null if the component type
91      *         is not present.
92      */
93     public <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem, final Class<K> listKey) {
94         for (PathArgument i : path) {
95             if (listItem.equals(i.getType())) {
96                 @SuppressWarnings("unchecked")
97                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
98                 return ret;
99             }
100         }
101
102         return null;
103     }
104
105     /**
106      * Return the key associated with the last component of the specified identifier.
107      *
108      * @param id instance identifier
109      * @return key associated with the last component
110      */
111     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
112         @SuppressWarnings("unchecked")
113         final K ret = ((IdentifiableItem<N, K>)Iterables.getLast(id.getPath())).getKey();
114         return ret;
115     }
116
117     /**
118      * Path argument of {@link InstanceIdentifier}.
119      * <p>
120      * Interface which implementations are used as path components of the
121      * path in overall data tree.
122      *
123      */
124     public interface PathArgument {
125
126         Class<? extends DataObject> getType();
127
128     }
129
130     public static final class Item<T extends DataObject> implements PathArgument {
131         private final Class<T> type;
132
133         public Item(Class<T> type) {
134             this.type = type;
135         }
136
137         @Override
138         public Class<T> getType() {
139             return type;
140         }
141
142         @Override
143         public int hashCode() {
144             final int prime = 31;
145             int result = 1;
146             result = prime * result + ((type == null) ? 0 : type.hashCode());
147             return result;
148         }
149
150         @Override
151         public boolean equals(Object obj) {
152             if (this == obj)
153                 return true;
154             if (obj == null)
155                 return false;
156             if (getClass() != obj.getClass())
157                 return false;
158             Item<?> other = (Item<?>) obj;
159             if (type == null) {
160                 if (other.type != null)
161                     return false;
162             } else if (!type.equals(other.type))
163                 return false;
164             return true;
165         }
166
167         @Override
168         public String toString() {
169             return type.getName();
170         }
171     }
172
173     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> implements
174             PathArgument {
175
176         private final T key;
177         private final Class<I> type;
178
179         public IdentifiableItem(Class<I> type, T key) {
180             if (type == null)
181                 throw new IllegalArgumentException("Type must not be null.");
182             if (key == null)
183                 throw new IllegalArgumentException("Key must not be null.");
184             this.type = type;
185             this.key = key;
186         }
187
188         public T getKey() {
189             return this.key;
190         }
191
192         @Override
193         public Class<I> getType() {
194             return this.type;
195         }
196
197         @Override
198         public boolean equals(Object obj) {
199             if (obj == null) {
200                 return false;
201             }
202             if (obj.hashCode() != hashCode()) {
203                 return false;
204             }
205             if (!(obj instanceof IdentifiableItem<?, ?>)) {
206                 return false;
207             }
208             IdentifiableItem<?, ?> foreign = (IdentifiableItem<?, ?>) obj;
209             return key.equals(foreign.getKey());
210         }
211
212         @Override
213         public int hashCode() {
214             return key.hashCode();
215         }
216
217         @Override
218         public String toString() {
219             return type.getName() + "[key=" + key + "]";
220         }
221     }
222
223     public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
224         /**
225          * @deprecated use {@link child(Class)} or {@link augmentation(Class)} instead.
226          */
227         @Deprecated
228         <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container);
229
230         /**
231          * @deprecated use {@link child(Class,Identifier)} or {@link augmentation(Class,Identifier)} instead.
232          */
233         @Deprecated
234         <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
235                 Class<N> listItem, K listKey);
236
237         <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
238
239         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
240                 Class<N> listItem, K listKey);
241
242         <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
243
244         InstanceIdentifier<T> build();
245
246     }
247
248     /**
249      * @deprecated use {@link builder(Class)} or {@link builder(Class,Identifier)} instead.
250      */
251     @Deprecated
252     @SuppressWarnings("rawtypes")
253     public static InstanceIdentifierBuilder<?> builder() {
254         return new BuilderImpl();
255     }
256
257     public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(Class<T> container) {
258         return new BuilderImpl<T>().addNode(container);
259     }
260
261     public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
262             Class<N> listItem, K listKey) {
263         return new BuilderImpl<N>().addNode(listItem, listKey);
264     }
265
266     public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(InstanceIdentifier<T> basePath) {
267         return new BuilderImpl<T>(basePath.path,basePath.targetType);
268     }
269
270     private static final class BuilderImpl<T extends DataObject> implements InstanceIdentifierBuilder<T> {
271
272         private final ImmutableList.Builder<PathArgument> path;
273         private Class<? extends DataObject> target = null;
274
275         public BuilderImpl() {
276             this.path = ImmutableList.builder();
277         }
278
279         public BuilderImpl(List<? extends PathArgument> prefix,Class<? extends DataObject> target) {
280             this.path = ImmutableList.<PathArgument>builder().addAll(prefix);
281             this.target = target;
282         }
283
284         @SuppressWarnings("unchecked")
285         private <N extends DataObject> InstanceIdentifierBuilder<N> addNode(Class<N> container) {
286             target = container;
287             path.add(new Item<N>(container));
288             return (InstanceIdentifierBuilder<N>) this;
289         }
290
291         @SuppressWarnings("unchecked")
292         private <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> addNode(
293                 Class<N> listItem, K listKey) {
294             target = listItem;
295             path.add(new IdentifiableItem<N, K>(listItem, listKey));
296             return (InstanceIdentifierBuilder<N>) this;
297         }
298
299         @SuppressWarnings({ "unchecked", "rawtypes" })
300         @Override
301         public InstanceIdentifier<T> toInstance() {
302             return new InstanceIdentifier(path.build(), target);
303         }
304
305         @Override
306         public InstanceIdentifier<T> build() {
307             return toInstance();
308         }
309
310         @Override
311         public <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container) {
312             return addNode(container);
313         }
314
315         @Override
316         public <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
317                 Class<N> listItem, K listKey) {
318             return addNode(listItem, listKey);
319         }
320
321         @Override
322         public <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container) {
323             return addNode(container);
324         }
325
326         @Override
327         public <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
328                 Class<N> listItem, K listKey) {
329             return addNode(listItem,listKey);
330         }
331
332         @Override
333         public <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(
334                 Class<N> container) {
335             return addNode(container);
336         }
337
338         @Override
339         public int hashCode() {
340             final int prime = 31;
341             int result = 1;
342             result = prime * result + ((path == null) ? 0 : path.hashCode());
343             return result;
344         }
345     }
346
347     @Override
348     public int hashCode() {
349         final int prime = 31;
350         int result = 1;
351         result = prime * result + ((path == null) ? 0 : path.hashCode());
352         return result;
353     }
354
355     @Override
356     public boolean equals(Object obj) {
357         if (this == obj) {
358             return true;
359         }
360         if (obj == null) {
361             return false;
362         }
363         if (getClass() != obj.getClass()) {
364             return false;
365         }
366         InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
367         if (path == null) {
368             if (other.path != null) {
369                 return false;
370             }
371         } else if (!path.equals(other.path)) {
372             return false;
373         }
374         return true;
375     }
376
377     @Override
378     public boolean contains(final InstanceIdentifier<?> other) {
379         if(other == null) {
380             throw new IllegalArgumentException("other should not be null");
381         }
382         final int localSize = this.path.size();
383         final List<PathArgument> otherPath = other.getPath();
384         if(localSize > other.path.size()) {
385             return false;
386         }
387         for(int i = 0;i<localSize;i++ ) {
388             if(!path.get(i).equals(otherPath.get(i))) {
389                 return false;
390             }
391         }
392         return true;
393     }
394
395     public boolean containsWildcarded(final InstanceIdentifier<?> other) {
396         if(other == null) {
397             throw new IllegalArgumentException("other should not be null");
398         }
399         final int localSize = this.path.size();
400         final List<PathArgument> otherPath = other.getPath();
401         if(localSize > other.path.size()) {
402             return false;
403         }
404         for(int i = 0;i<localSize;i++ ) {
405             final PathArgument localArgument = path.get(i);
406             final PathArgument otherArgument = otherPath.get(i);
407             if(!localArgument.getType().equals(otherArgument.getType())) {
408                 return false;
409             }
410             if(localArgument instanceof IdentifiableItem<?, ?> && otherArgument instanceof IdentifiableItem<?, ?> && !localArgument.equals(otherPath.get(i))) {
411                 return false;
412             }
413         }
414         return true;
415     }
416
417     public boolean isWildcarded() {
418         for(PathArgument pathArgument : path) {
419             if(Identifiable.class.isAssignableFrom(pathArgument.getType()) && !(pathArgument instanceof IdentifiableItem<?, ?>)) {
420                 return true;
421             }
422         }
423         return false;
424     }
425 }