Add a few useful utilities 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 this.path;
47     }
48
49     public Class<T> getTargetType() {
50         return this.targetType;
51     }
52
53     @Override
54     public String toString() {
55         return "InstanceIdentifier [path=" + path + "]";
56     }
57
58     /**
59      * Return an instance identifier trimmed at the first occurrence of a
60      * specific component type.
61      * 
62      * @param type component type
63      * @return trimmed instance identifier, or null if the component type
64      *         is not present.
65      */
66     public <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final Class<T> type) {
67         int i = 1;
68         for (final PathArgument a : path) {
69             if (type.equals(a.getType())) {
70                 return new InstanceIdentifier<>(path.subList(0, i), type);
71             }
72
73             ++i;
74         }
75
76         return null;
77     }
78
79     /**
80      * Return the key associated with the first component of specified type in
81      * an identifier.
82      * 
83      * @param listItem component type
84      * @param listKey component key type
85      * @return key associated with the component, or null if the component type
86      *         is not present.
87      */
88     public <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem, final Class<K> listKey) {
89         for (PathArgument i : path) {
90             if (listItem.equals(i.getType())) {
91                 @SuppressWarnings("unchecked")
92                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
93                 return ret;
94             }
95         }
96
97         return null;
98     }
99
100     /**
101      * Return the key associated with the last component of the specified identifier.
102      * 
103      * @param id instance identifier
104      * @return key associated with the last component
105      */
106     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
107         @SuppressWarnings("unchecked")
108         final K ret = ((IdentifiableItem<N, K>)Iterables.getLast(id.getPath())).getKey();
109         return ret;
110     }
111
112     /**
113      * Path argument of {@link InstanceIdentifier}.
114      * <p>
115      * Interface which implementations are used as path components of the
116      * path in overall data tree.
117      *
118      */
119     public interface PathArgument {
120
121         Class<? extends DataObject> getType();
122
123     }
124
125     public static final class Item<T extends DataObject> implements PathArgument {
126         private final Class<T> type;
127
128         public Item(Class<T> type) {
129             this.type = type;
130         }
131
132         public Class<T> getType() {
133             return type;
134         }
135
136         @Override
137         public int hashCode() {
138             final int prime = 31;
139             int result = 1;
140             result = prime * result + ((type == null) ? 0 : type.hashCode());
141             return result;
142         }
143
144         @Override
145         public boolean equals(Object obj) {
146             if (this == obj)
147                 return true;
148             if (obj == null)
149                 return false;
150             if (getClass() != obj.getClass())
151                 return false;
152             Item<?> other = (Item<?>) obj;
153             if (type == null) {
154                 if (other.type != null)
155                     return false;
156             } else if (!type.equals(other.type))
157                 return false;
158             return true;
159         }
160
161         @Override
162         public String toString() {
163             return type.getName();
164         }
165     }
166
167     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> implements
168             PathArgument {
169
170         private final T key;
171         private final Class<I> type;
172
173         public IdentifiableItem(Class<I> type, T key) {
174             if (type == null)
175                 throw new IllegalArgumentException("Type must not be null.");
176             if (key == null)
177                 throw new IllegalArgumentException("Key must not be null.");
178             this.type = type;
179             this.key = key;
180         }
181
182         public T getKey() {
183             return this.key;
184         }
185
186         @Override
187         public Class<I> getType() {
188             return this.type;
189         }
190
191         @Override
192         public boolean equals(Object obj) {
193             if (obj == null) {
194                 return false;
195             }
196             if (obj.hashCode() != hashCode()) {
197                 return false;
198             }
199             if (!(obj instanceof IdentifiableItem<?, ?>)) {
200                 return false;
201             }
202             IdentifiableItem<?, ?> foreign = (IdentifiableItem<?, ?>) obj;
203             return key.equals(foreign.getKey());
204         }
205
206         @Override
207         public int hashCode() {
208             return key.hashCode();
209         }
210
211         @Override
212         public String toString() {
213             return type.getName() + "[key=" + key + "]";
214         }
215     }
216
217     public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
218         /**
219          * @deprecated use {@link child(Class)} or {@link augmentation(Class)} instead.
220          */
221         @Deprecated
222         <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container);
223
224         /**
225          * @deprecated use {@link child(Class,Identifier)} or {@link augmentation(Class,Identifier)} instead.
226          */
227         @Deprecated
228         <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
229                 Class<N> listItem, K listKey);
230
231         <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
232
233         <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
234                 Class<N> listItem, K listKey);
235
236         <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
237
238     }
239
240     /**
241      * @deprecated use {@link builder(Class)} or {@link builder(Class,Identifier)} instead.
242      */
243     @Deprecated
244     @SuppressWarnings("rawtypes")
245     public static InstanceIdentifierBuilder<?> builder() {
246         return new BuilderImpl();
247     }
248
249     public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(Class<T> container) {
250         return new BuilderImpl<T>().addNode(container);
251     }
252
253     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
254             Class<N> listItem, K listKey) {
255         return new BuilderImpl<N>().addNode(listItem, listKey);
256     }
257
258     public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(InstanceIdentifier<T> basePath) {
259         return new BuilderImpl<T>(basePath.path,basePath.targetType);
260     }
261
262     private static final class BuilderImpl<T extends DataObject> implements InstanceIdentifierBuilder<T> {
263
264         private List<PathArgument> path;
265         private Class<? extends DataObject> target = null;
266
267         public BuilderImpl() {
268             this.path = new ArrayList<>();
269         }
270
271         public BuilderImpl(List<? extends PathArgument> prefix,Class<? extends DataObject> target) {
272             this.path = new ArrayList<>(prefix);
273             this.target = target;
274         }
275
276         @SuppressWarnings("unchecked")
277         private <N extends DataObject> InstanceIdentifierBuilder<N> addNode(Class<N> container) {
278             target = container;
279             path.add(new Item<N>(container));
280             return (InstanceIdentifierBuilder<N>) this;
281         }
282
283         @SuppressWarnings("unchecked")
284         private <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> addNode(
285                 Class<N> listItem, K listKey) {
286             target = listItem;
287             path.add(new IdentifiableItem<N, K>(listItem, listKey));
288             return (InstanceIdentifierBuilder<N>) this;
289         }
290
291         @SuppressWarnings({ "unchecked", "rawtypes" })
292         @Override
293         public InstanceIdentifier<T> toInstance() {
294             List<PathArgument> immutablePath = Collections.unmodifiableList(new ArrayList<PathArgument>(path));
295             return new InstanceIdentifier(immutablePath, target);
296         }
297
298         @Override
299         public <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container) {
300             return addNode(container);
301         }
302
303         @Override
304         public <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
305                 Class<N> listItem, K listKey) {
306             return addNode(listItem, listKey);
307         }
308
309         @Override
310         public <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container) {
311             return addNode(container);
312         }
313
314         @Override
315         public <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
316                 Class<N> listItem, K listKey) {
317             return addNode(listItem,listKey);
318         }
319
320         @Override
321         public <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(
322                 Class<N> container) {
323             return addNode(container);
324         }
325     }
326
327     @Override
328     public int hashCode() {
329         final int prime = 31;
330         int result = 1;
331         result = prime * result + ((path == null) ? 0 : path.hashCode());
332         return result;
333     }
334
335     @Override
336     public boolean equals(Object obj) {
337         if (this == obj) {
338             return true;
339         }
340         if (obj == null) {
341             return false;
342         }
343         if (getClass() != obj.getClass()) {
344             return false;
345         }
346         InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
347         if (path == null) {
348             if (other.path != null) {
349                 return false;
350             }
351         } else if (!path.equals(other.path)) {
352             return false;
353         }
354         return true;
355     }
356
357     @Override
358     public boolean contains(final InstanceIdentifier<?> other) {
359         if(other == null) {
360             throw new IllegalArgumentException("other should not be null");
361         }
362         final int localSize = this.path.size();
363         final List<PathArgument> otherPath = other.getPath();
364         if(localSize > other.path.size()) {
365             return false;
366         }
367         for(int i = 0;i<localSize;i++ ) {
368             if(!path.get(i).equals(otherPath.get(i))) {
369                 return false;
370             }
371         }
372
373         return true;
374     }
375 }