Added wildcarded contains to InstanceIdentifier
[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.ArrayList;
11 import java.util.Collections;
12 import java.util.List;
13
14 import org.opendaylight.yangtools.concepts.Builder;
15 import org.opendaylight.yangtools.concepts.Immutable;
16 import org.opendaylight.yangtools.concepts.Path;
17
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 = Collections.<PathArgument> unmodifiableList(new ArrayList<>(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     public <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final Class<T> type) {
71         int i = 1;
72         for (final PathArgument a : path) {
73             if (type.equals(a.getType())) {
74                 return new InstanceIdentifier<>(path.subList(0, i), type);
75             }
76
77             ++i;
78         }
79
80         return null;
81     }
82
83     /**
84      * Return the key associated with the first component of specified type in
85      * an identifier.
86      * 
87      * @param listItem component type
88      * @param listKey component key type
89      * @return key associated with the component, or null if the component type
90      *         is not present.
91      */
92     public <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem, final Class<K> listKey) {
93         for (PathArgument i : path) {
94             if (listItem.equals(i.getType())) {
95                 @SuppressWarnings("unchecked")
96                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
97                 return ret;
98             }
99         }
100
101         return null;
102     }
103
104     /**
105      * Return the key associated with the last component of the specified identifier.
106      * 
107      * @param id instance identifier
108      * @return key associated with the last component
109      */
110     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
111         @SuppressWarnings("unchecked")
112         final K ret = ((IdentifiableItem<N, K>)Iterables.getLast(id.getPath())).getKey();
113         return ret;
114     }
115
116     /**
117      * Path argument of {@link InstanceIdentifier}.
118      * <p>
119      * Interface which implementations are used as path components of the
120      * path in overall data tree.
121      *
122      */
123     public interface PathArgument {
124
125         Class<? extends DataObject> getType();
126
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         public Class<T> getType() {
137             return type;
138         }
139
140         @Override
141         public int hashCode() {
142             final int prime = 31;
143             int result = 1;
144             result = prime * result + ((type == null) ? 0 : type.hashCode());
145             return result;
146         }
147
148         @Override
149         public boolean equals(Object obj) {
150             if (this == obj)
151                 return true;
152             if (obj == null)
153                 return false;
154             if (getClass() != obj.getClass())
155                 return false;
156             Item<?> other = (Item<?>) obj;
157             if (type == null) {
158                 if (other.type != null)
159                     return false;
160             } else if (!type.equals(other.type))
161                 return false;
162             return true;
163         }
164
165         @Override
166         public String toString() {
167             return type.getName();
168         }
169     }
170
171     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> implements
172             PathArgument {
173
174         private final T key;
175         private final Class<I> type;
176
177         public IdentifiableItem(Class<I> type, T key) {
178             if (type == null)
179                 throw new IllegalArgumentException("Type must not be null.");
180             if (key == null)
181                 throw new IllegalArgumentException("Key must not be null.");
182             this.type = type;
183             this.key = key;
184         }
185
186         public T getKey() {
187             return this.key;
188         }
189
190         @Override
191         public Class<I> getType() {
192             return this.type;
193         }
194
195         @Override
196         public boolean equals(Object obj) {
197             if (obj == null) {
198                 return false;
199             }
200             if (obj.hashCode() != hashCode()) {
201                 return false;
202             }
203             if (!(obj instanceof IdentifiableItem<?, ?>)) {
204                 return false;
205             }
206             IdentifiableItem<?, ?> foreign = (IdentifiableItem<?, ?>) obj;
207             return key.equals(foreign.getKey());
208         }
209
210         @Override
211         public int hashCode() {
212             return key.hashCode();
213         }
214
215         @Override
216         public String toString() {
217             return type.getName() + "[key=" + key + "]";
218         }
219     }
220
221     public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
222         /**
223          * @deprecated use {@link child(Class)} or {@link augmentation(Class)} instead.
224          */
225         @Deprecated
226         <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container);
227
228         /**
229          * @deprecated use {@link child(Class,Identifier)} or {@link augmentation(Class,Identifier)} instead.
230          */
231         @Deprecated
232         <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
233                 Class<N> listItem, K listKey);
234
235         <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
236
237         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
238                 Class<N> listItem, K listKey);
239
240         <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
241         
242         InstanceIdentifier<T> build();
243
244     }
245
246     /**
247      * @deprecated use {@link builder(Class)} or {@link builder(Class,Identifier)} instead.
248      */
249     @Deprecated
250     @SuppressWarnings("rawtypes")
251     public static InstanceIdentifierBuilder<?> builder() {
252         return new BuilderImpl();
253     }
254
255     public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(Class<T> container) {
256         return new BuilderImpl<T>().addNode(container);
257     }
258
259     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
260             Class<N> listItem, K listKey) {
261         return new BuilderImpl<N>().addNode(listItem, listKey);
262     }
263
264     public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(InstanceIdentifier<T> basePath) {
265         return new BuilderImpl<T>(basePath.path,basePath.targetType);
266     }
267
268     private static final class BuilderImpl<T extends DataObject> implements InstanceIdentifierBuilder<T> {
269
270         private List<PathArgument> path;
271         private Class<? extends DataObject> target = null;
272
273         public BuilderImpl() {
274             this.path = new ArrayList<>();
275         }
276
277         public BuilderImpl(List<? extends PathArgument> prefix,Class<? extends DataObject> target) {
278             this.path = new ArrayList<>(prefix);
279             this.target = target;
280         }
281
282         @SuppressWarnings("unchecked")
283         private <N extends DataObject> InstanceIdentifierBuilder<N> addNode(Class<N> container) {
284             target = container;
285             path.add(new Item<N>(container));
286             return (InstanceIdentifierBuilder<N>) this;
287         }
288
289         @SuppressWarnings("unchecked")
290         private <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> addNode(
291                 Class<N> listItem, K listKey) {
292             target = listItem;
293             path.add(new IdentifiableItem<N, K>(listItem, listKey));
294             return (InstanceIdentifierBuilder<N>) this;
295         }
296
297         @SuppressWarnings({ "unchecked", "rawtypes" })
298         @Override
299         public InstanceIdentifier<T> toInstance() {
300             List<PathArgument> immutablePath = Collections.unmodifiableList(new ArrayList<PathArgument>(path));
301             return new InstanceIdentifier(immutablePath, 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
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     @Override
347     public boolean equals(Object obj) {
348         if (this == obj) {
349             return true;
350         }
351         if (obj == null) {
352             return false;
353         }
354         if (getClass() != obj.getClass()) {
355             return false;
356         }
357         InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
358         if (path == null) {
359             if (other.path != null) {
360                 return false;
361             }
362         } else if (!path.equals(other.path)) {
363             return false;
364         }
365         return true;
366     }
367
368     @Override
369     public boolean contains(final InstanceIdentifier<?> other) {
370         if(other == null) {
371             throw new IllegalArgumentException("other should not be null");
372         }
373         final int localSize = this.path.size();
374         final List<PathArgument> otherPath = other.getPath();
375         if(localSize > other.path.size()) {
376             return false;
377         }
378         for(int i = 0;i<localSize;i++ ) {
379             if(!path.get(i).equals(otherPath.get(i))) {
380                 return false;
381             }
382         }
383         return true;
384     }
385     
386     public boolean containsWildcarded(final InstanceIdentifier<?> other) {
387         if(other == null) {
388             throw new IllegalArgumentException("other should not be null");
389         }
390         final int localSize = this.path.size();
391         final List<PathArgument> otherPath = other.getPath();
392         if(localSize > other.path.size()) {
393             return false;
394         }
395         for(int i = 0;i<localSize;i++ ) {
396             final PathArgument localArgument = path.get(i);
397             if(!localArgument.getType().equals(otherPath.get(i).getType())) {
398                 return false;
399             } 
400             if(localArgument instanceof IdentifiableItem<?, ?> && !localArgument.equals(otherPath.get(i))) {
401                 return false;
402             }
403         }
404         return true;
405     }
406
407     public boolean isWildcarded() {
408         for(PathArgument pathArgument : path) {
409             if(Identifiable.class.isAssignableFrom(pathArgument.getType()) && !(pathArgument instanceof IdentifiableItem<?, ?>)) {
410                 return true;
411             }
412         }
413         return false;
414     }
415 }