Merge "Fixed possible NPE in CodecMapping."
[yangtools.git] / yang / yang-data-api / src / main / java / org / opendaylight / yangtools / yang / data / api / InstanceIdentifier.java
1 /*
2  * Copyright (c) 2014 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.data.api;
9
10 import java.io.Serializable;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14
15 import org.opendaylight.yangtools.concepts.Builder;
16 import org.opendaylight.yangtools.concepts.Immutable;
17 import org.opendaylight.yangtools.concepts.Path;
18 import org.opendaylight.yangtools.yang.common.QName;
19 import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode;
20 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
21 import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
22 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
23 import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode;
24
25 import com.google.common.collect.ImmutableList;
26 import com.google.common.collect.ImmutableMap;
27 import com.google.common.collect.ImmutableSet;
28
29 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
30
31     private static final long serialVersionUID = 8467409862384206193L;
32     private final List<PathArgument> path;
33
34     private transient String toStringCache = null;
35     private transient Integer hashCodeCache = null;
36
37     public List<PathArgument> getPath() {
38         return path;
39     }
40
41     public InstanceIdentifier(final List<? extends PathArgument> path) {
42         this.path = ImmutableList.copyOf(path);
43     }
44
45     private InstanceIdentifier(NodeIdentifier nodeIdentifier) {
46         this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
47     }
48
49     @Override
50     public int hashCode() {
51         /*
52          * The hashCodeCache is safe, since the object contract requires
53          * immutability of the object and all objects referenced from this
54          * object.
55          *
56          * Used lists, maps are immutable. Path Arguments (elements) are also
57          * immutable, since the PathArgument contract requires immutability.
58          *
59          * The cache is thread-safe - if multiple computations occurs at the
60          * same time, cache will be overwritten with same result.
61          */
62         if (hashCodeCache == null) {
63             final int prime = 31;
64             int result = 1;
65             result = prime * result + ((path == null) ? 0 : path.hashCode());
66             hashCodeCache = result;
67         }
68         return hashCodeCache;
69     }
70
71     @Override
72     public boolean equals(Object obj) {
73         if (this == obj) {
74             return true;
75         }
76         if (obj == null) {
77             return false;
78         }
79         if (getClass() != obj.getClass()) {
80             return false;
81         }
82         InstanceIdentifier other = (InstanceIdentifier) obj;
83         if (this.hashCode() != obj.hashCode()) {
84             return false;
85         }
86         if (path == null) {
87             if (other.path != null) {
88                 return false;
89             }
90         } else if (!path.equals(other.path)) {
91             return false;
92         }
93         return true;
94     }
95
96     // Static factories & helpers
97
98     public static InstanceIdentifier of(QName name) {
99         return new InstanceIdentifier(new NodeIdentifier(name));
100     }
101
102     static public InstanceIdentifierBuilder builder() {
103         return new BuilderImpl();
104     }
105
106     static public InstanceIdentifierBuilder builder(InstanceIdentifier origin) {
107         return new BuilderImpl(origin.getPath());
108     }
109
110     public interface PathArgument extends Immutable, Serializable {
111
112         /**
113          * If applicable returns uniqee QName of data node as defined in YANG
114          * Schema.
115          *
116          * This method may return null, if the corresponding schema node, does
117          * not have QName associated, such as in cases of augmentations.
118          *
119          * @return
120          */
121         QName getNodeType();
122
123     }
124
125     public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
126         InstanceIdentifierBuilder node(QName nodeType);
127
128         InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
129
130         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
131
132         @Deprecated
133         InstanceIdentifier getIdentifier();
134
135         InstanceIdentifier build();
136     }
137
138     /**
139      * Simple path argument identifying a {@link ContainerNode} or {@link LeafNode} leaf
140      * overal data tree.
141      *
142      */
143     public static final class NodeIdentifier implements PathArgument {
144
145         /**
146          *
147          */
148         private static final long serialVersionUID = -2255888212390871347L;
149
150         private final QName nodeType;
151
152         public NodeIdentifier(QName node) {
153             this.nodeType = node;
154         }
155
156         @Override
157         public QName getNodeType() {
158             return nodeType;
159         }
160
161         @Override
162         public int hashCode() {
163             final int prime = 31;
164             int result = 1;
165             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
166             return result;
167         }
168
169         @Override
170         public boolean equals(Object obj) {
171             if (this == obj)
172                 return true;
173             if (obj == null)
174                 return false;
175             if (getClass() != obj.getClass())
176                 return false;
177             NodeIdentifier other = (NodeIdentifier) obj;
178             if (nodeType == null) {
179                 if (other.nodeType != null)
180                     return false;
181             } else if (!nodeType.equals(other.nodeType))
182                 return false;
183             return true;
184         }
185
186         @Override
187         public String toString() {
188             return nodeType.toString();
189         }
190     }
191
192     /**
193      *
194      * Composite path argument identifying a {@link MapEntryNode} leaf
195      * overal data tree.
196      *
197      */
198     public static final class NodeIdentifierWithPredicates implements PathArgument {
199
200         /**
201          *
202          */
203         private static final long serialVersionUID = -4787195606494761540L;
204
205         private final QName nodeType;
206         private final Map<QName, Object> keyValues;
207
208         public NodeIdentifierWithPredicates(QName node, Map<QName, Object> keyValues) {
209             this.nodeType = node;
210             this.keyValues = ImmutableMap.copyOf(keyValues);
211         }
212
213         public NodeIdentifierWithPredicates(QName node, QName key, Object value) {
214             this.nodeType = node;
215             this.keyValues = ImmutableMap.of(key, value);
216         }
217
218         @Override
219         public QName getNodeType() {
220             return nodeType;
221         }
222
223         public Map<QName, Object> getKeyValues() {
224             return keyValues;
225         }
226
227         @Override
228         public int hashCode() {
229             final int prime = 31;
230             int result = 1;
231             result = prime * result + ((keyValues == null) ? 0 : keyValues.hashCode());
232             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
233             return result;
234         }
235
236         @Override
237         public boolean equals(Object obj) {
238             if (this == obj)
239                 return true;
240             if (obj == null)
241                 return false;
242             if (getClass() != obj.getClass())
243                 return false;
244             NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
245             if (keyValues == null) {
246                 if (other.keyValues != null)
247                     return false;
248             } else if (!keyValues.equals(other.keyValues))
249                 return false;
250             if (nodeType == null) {
251                 if (other.nodeType != null)
252                     return false;
253             } else if (!nodeType.equals(other.nodeType))
254                 return false;
255             return true;
256         }
257
258         @Override
259         public String toString() {
260             return nodeType + "[" + keyValues + "]";
261         }
262     }
263
264     /**
265      * Simple path argument identifying a {@link LeafSetEntryNode} leaf
266      * overal data tree.
267      *
268      */
269     public static final class NodeWithValue implements PathArgument {
270
271        /**
272         *
273         * Composite path argument identifying a {@link AugmentationNode} leaf
274         * overal data tree.
275         *
276         */
277         private static final long serialVersionUID = -3637456085341738431L;
278
279         private final QName nodeType;
280         private final Object value;
281
282         public NodeWithValue(QName node, Object value) {
283             this.nodeType = node;
284             this.value = value;
285         }
286
287         @Override
288         public QName getNodeType() {
289             return nodeType;
290         }
291
292         public Object getValue() {
293             return value;
294         }
295
296         @Override
297         public int hashCode() {
298             final int prime = 31;
299             int result = 1;
300             result = prime * result + ((value == null) ? 0 : value.hashCode());
301             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
302             return result;
303         }
304
305         @Override
306         public boolean equals(Object obj) {
307             if (this == obj)
308                 return true;
309             if (obj == null)
310                 return false;
311             if (getClass() != obj.getClass())
312                 return false;
313             NodeWithValue other = (NodeWithValue) obj;
314             if (value == null) {
315                 if (other.value != null)
316                     return false;
317             } else if (!value.equals(other.value))
318                 return false;
319             if (nodeType == null) {
320                 if (other.nodeType != null)
321                     return false;
322             } else if (!nodeType.equals(other.nodeType))
323                 return false;
324             return true;
325         }
326
327         @Override
328         public String toString() {
329             return nodeType + "[" + value + "]";
330         }
331
332     }
333
334
335     public static final class AugmentationIdentifier implements PathArgument {
336
337
338         private static final long serialVersionUID = -8122335594681936939L;
339         private final QName nodeType;
340         private final ImmutableSet<QName> childNames;
341
342         @Override
343         public QName getNodeType() {
344             return nodeType;
345         }
346
347         public AugmentationIdentifier(QName nodeType, Set<QName> childNames) {
348             super();
349             this.nodeType = nodeType;
350             this.childNames = ImmutableSet.copyOf(childNames);
351         }
352
353         public Set<QName> getPossibleChildNames() {
354             return childNames;
355         }
356
357     }
358
359     private static class BuilderImpl implements InstanceIdentifierBuilder {
360
361         private final ImmutableList.Builder<PathArgument> path;
362
363         public BuilderImpl() {
364             path = ImmutableList.<PathArgument> builder();
365         }
366
367         public BuilderImpl(List<? extends PathArgument> prefix) {
368             path = ImmutableList.<PathArgument> builder();
369             path.addAll(prefix);
370         }
371
372         @Override
373         public InstanceIdentifierBuilder node(QName nodeType) {
374             path.add(new NodeIdentifier(nodeType));
375             return this;
376         }
377
378         @Override
379         public InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value) {
380             path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
381             return this;
382         }
383
384         @Override
385         public InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues) {
386             path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
387             return this;
388         }
389
390         @Override
391         @Deprecated
392         public InstanceIdentifier toInstance() {
393             return build();
394         }
395
396         @Override
397         public InstanceIdentifier build() {
398             return new InstanceIdentifier(path.build());
399         }
400
401         @Override
402         @Deprecated
403         public InstanceIdentifier getIdentifier() {
404             return build();
405         }
406     }
407
408     @Override
409     public boolean contains(final InstanceIdentifier other) {
410         if (other == null) {
411             throw new IllegalArgumentException("other should not be null");
412         }
413         final int localSize = this.path.size();
414         final List<PathArgument> otherPath = other.getPath();
415         if (localSize > other.path.size()) {
416             return false;
417         }
418         for (int i = 0; i < localSize; i++) {
419             if (!path.get(i).equals(otherPath.get(i))) {
420                 return false;
421             }
422         }
423         return true;
424     }
425
426     @Override
427     public String toString() {
428         /*
429          * The toStringCache is safe, since the object contract requires
430          * immutability of the object and all objects referenced from this
431          * object.
432          *
433          * Used lists, maps are immutable. Path Arguments (elements) are also
434          * immutable, since the PathArgument contract requires immutability.
435          *
436          * The cache is thread-safe - if multiple computations occurs at the
437          * same time, cache will be overwritten with same result.
438          */
439         if (toStringCache != null) {
440             return toStringCache;
441         }
442         StringBuilder builder = new StringBuilder();
443         for (PathArgument argument : path) {
444             builder.append("/");
445             builder.append(argument.toString());
446         }
447         toStringCache = builder.toString();
448         return toStringCache;
449     }
450
451     public static InstanceIdentifierBuilder builder(QName node) {
452         return builder().node(node);
453     }
454 }