Merge "Fix for Bug 496."
[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     public static final class Item<T extends DataObject> implements PathArgument {
130         private final Class<T> type;
131
132         public Item(Class<T> type) {
133             this.type = type;
134         }
135
136         @Override
137         public Class<T> getType() {
138             return type;
139         }
140
141         @Override
142         public int hashCode() {
143             final int prime = 31;
144             int result = 1;
145             result = prime * result + ((type == null) ? 0 : type.hashCode());
146             return result;
147         }
148
149         @Override
150         public boolean equals(Object obj) {
151             if (this == obj)
152                 return true;
153             if (obj == null)
154                 return false;
155             if (getClass() != obj.getClass())
156                 return false;
157             Item<?> other = (Item<?>) obj;
158             if (type == null) {
159                 if (other.type != null)
160                     return false;
161             } else if (!type.equals(other.type))
162                 return false;
163             return true;
164         }
165
166         @Override
167         public String toString() {
168             return type.getName();
169         }
170     }
171
172     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> implements
173             PathArgument {
174
175         private final T key;
176         private final Class<I> type;
177
178         public IdentifiableItem(Class<I> type, T key) {
179             if (type == null)
180                 throw new IllegalArgumentException("Type must not be null.");
181             if (key == null)
182                 throw new IllegalArgumentException("Key must not be null.");
183             this.type = type;
184             this.key = key;
185         }
186
187         public T getKey() {
188             return this.key;
189         }
190
191         @Override
192         public Class<I> getType() {
193             return this.type;
194         }
195
196         @Override
197         public boolean equals(Object obj) {
198             if (obj == null) {
199                 return false;
200             }
201             if (obj.hashCode() != hashCode()) {
202                 return false;
203             }
204             if (!(obj instanceof IdentifiableItem<?, ?>)) {
205                 return false;
206             }
207             IdentifiableItem<?, ?> foreign = (IdentifiableItem<?, ?>) obj;
208             return key.equals(foreign.getKey());
209         }
210
211         @Override
212         public int hashCode() {
213             return key.hashCode();
214         }
215
216         @Override
217         public String toString() {
218             return type.getName() + "[key=" + key + "]";
219         }
220     }
221
222     public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
223         /**
224          * @deprecated use {@link child(Class)} or {@link augmentation(Class)} instead.
225          */
226         @Deprecated
227         <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container);
228
229         /**
230          * @deprecated use {@link child(Class,Identifier)} or {@link augmentation(Class,Identifier)} instead.
231          */
232         @Deprecated
233         <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
234                 Class<N> listItem, K listKey);
235
236         <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
237
238         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
239                 Class<N> listItem, K listKey);
240
241         <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
242
243         InstanceIdentifier<T> build();
244
245     }
246
247     /**
248      * @deprecated use {@link builder(Class)} or {@link builder(Class,Identifier)} instead.
249      */
250     @Deprecated
251     @SuppressWarnings("rawtypes")
252     public static InstanceIdentifierBuilder<?> builder() {
253         return new BuilderImpl();
254     }
255
256     public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(Class<T> container) {
257         return new BuilderImpl<T>().addNode(container);
258     }
259
260     public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
261             Class<N> listItem, K listKey) {
262         return new BuilderImpl<N>().addNode(listItem, listKey);
263     }
264
265     public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(InstanceIdentifier<T> basePath) {
266         return new BuilderImpl<T>(basePath.path,basePath.targetType);
267     }
268
269     private static final class BuilderImpl<T extends DataObject> implements InstanceIdentifierBuilder<T> {
270
271         private final ImmutableList.Builder<PathArgument> path;
272         private Class<? extends DataObject> target = null;
273
274         public BuilderImpl() {
275             this.path = ImmutableList.builder();
276         }
277
278         public BuilderImpl(List<? extends PathArgument> prefix,Class<? extends DataObject> target) {
279             this.path = ImmutableList.<PathArgument>builder().addAll(prefix);
280             this.target = target;
281         }
282
283         @SuppressWarnings("unchecked")
284         private <N extends DataObject> InstanceIdentifierBuilder<N> addNode(Class<N> container) {
285             target = container;
286             path.add(new Item<N>(container));
287             return (InstanceIdentifierBuilder<N>) this;
288         }
289
290         @SuppressWarnings("unchecked")
291         private <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> addNode(
292                 Class<N> listItem, K listKey) {
293             target = listItem;
294             path.add(new IdentifiableItem<N, K>(listItem, listKey));
295             return (InstanceIdentifierBuilder<N>) this;
296         }
297
298         @SuppressWarnings({ "unchecked", "rawtypes" })
299         @Override
300         public InstanceIdentifier<T> toInstance() {
301             return new InstanceIdentifier(path.build(), target);
302         }
303
304         @Override
305         public InstanceIdentifier<T> build() {
306             return toInstance();
307         }
308
309         @Override
310         public <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container) {
311             return addNode(container);
312         }
313
314         @Override
315         public <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
316                 Class<N> listItem, K listKey) {
317             return addNode(listItem, listKey);
318         }
319
320         @Override
321         public <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container) {
322             return addNode(container);
323         }
324
325         @Override
326         public <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
327                 Class<N> listItem, K listKey) {
328             return addNode(listItem,listKey);
329         }
330
331         @Override
332         public <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(
333                 Class<N> container) {
334             return addNode(container);
335         }
336
337         @Override
338         public int hashCode() {
339             final int prime = 31;
340             int result = 1;
341             result = prime * result + ((path == null) ? 0 : path.hashCode());
342             return result;
343         }
344     }
345
346     @Override
347     public int hashCode() {
348         final int prime = 31;
349         int result = 1;
350         result = prime * result + ((path == null) ? 0 : path.hashCode());
351         return result;
352     }
353
354     @Override
355     public boolean equals(Object obj) {
356         if (this == obj) {
357             return true;
358         }
359         if (obj == null) {
360             return false;
361         }
362         if (getClass() != obj.getClass()) {
363             return false;
364         }
365         InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
366         if (path == null) {
367             if (other.path != null) {
368                 return false;
369             }
370         } else if (!path.equals(other.path)) {
371             return false;
372         }
373         return true;
374     }
375
376     @Override
377     public boolean contains(final InstanceIdentifier<?> other) {
378         if(other == null) {
379             throw new IllegalArgumentException("other should not be null");
380         }
381         final int localSize = this.path.size();
382         final List<PathArgument> otherPath = other.getPath();
383         if(localSize > other.path.size()) {
384             return false;
385         }
386         for(int i = 0;i<localSize;i++ ) {
387             if(!path.get(i).equals(otherPath.get(i))) {
388                 return false;
389             }
390         }
391         return true;
392     }
393
394     public boolean containsWildcarded(final InstanceIdentifier<?> other) {
395         if(other == null) {
396             throw new IllegalArgumentException("other should not be null");
397         }
398         final int localSize = this.path.size();
399         final List<PathArgument> otherPath = other.getPath();
400         if(localSize > other.path.size()) {
401             return false;
402         }
403         for(int i = 0;i<localSize;i++ ) {
404             final PathArgument localArgument = path.get(i);
405             final PathArgument otherArgument = otherPath.get(i);
406             if(!localArgument.getType().equals(otherArgument.getType())) {
407                 return false;
408             }
409             if(localArgument instanceof IdentifiableItem<?, ?> && otherArgument instanceof IdentifiableItem<?, ?> && !localArgument.equals(otherPath.get(i))) {
410                 return false;
411             }
412         }
413         return true;
414     }
415
416     public boolean isWildcarded() {
417         for(PathArgument pathArgument : path) {
418             if(Identifiable.class.isAssignableFrom(pathArgument.getType()) && !(pathArgument instanceof IdentifiableItem<?, ?>)) {
419                 return true;
420             }
421         }
422         return false;
423     }
424 }