Bug 735 - Part 1: Update ietf-restconf and ietf-yangtypes to newer versions
[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.base.Optional;
26 import com.google.common.collect.ImmutableList;
27 import com.google.common.collect.ImmutableMap;
28 import com.google.common.collect.ImmutableSet;
29
30 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
31
32     private static final long serialVersionUID = 8467409862384206193L;
33     private final List<PathArgument> path;
34
35     private transient String toStringCache = null;
36     private transient Integer hashCodeCache = null;
37
38     public List<PathArgument> getPath() {
39         return path;
40     }
41
42     public InstanceIdentifier(final List<? extends PathArgument> path) {
43         this.path = ImmutableList.copyOf(path);
44     }
45
46     private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
47         this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
48     }
49
50     @Override
51     public int hashCode() {
52         /*
53          * The hashCodeCache is safe, since the object contract requires
54          * immutability of the object and all objects referenced from this
55          * object.
56          *
57          * Used lists, maps are immutable. Path Arguments (elements) are also
58          * immutable, since the PathArgument contract requires immutability.
59          *
60          * The cache is thread-safe - if multiple computations occurs at the
61          * same time, cache will be overwritten with same result.
62          */
63         if (hashCodeCache == null) {
64             final int prime = 31;
65             int result = 1;
66             result = prime * result + ((path == null) ? 0 : path.hashCode());
67             hashCodeCache = result;
68         }
69         return hashCodeCache;
70     }
71
72     @Override
73     public boolean equals(final Object obj) {
74         if (this == obj) {
75             return true;
76         }
77         if (obj == null) {
78             return false;
79         }
80         if (getClass() != obj.getClass()) {
81             return false;
82         }
83         InstanceIdentifier other = (InstanceIdentifier) obj;
84         if (this.hashCode() != obj.hashCode()) {
85             return false;
86         }
87         if (path == null) {
88             if (other.path != null) {
89                 return false;
90             }
91         } else if (!path.equals(other.path)) {
92             return false;
93         }
94         return true;
95     }
96
97     public InstanceIdentifier node(final QName name) {
98         return node(new NodeIdentifier(name));
99     }
100
101     public InstanceIdentifier node(final PathArgument arg) {
102         return new InstanceIdentifier(ImmutableList.<PathArgument>builder().addAll(path).add(arg).build());
103     }
104
105     /**
106      * Get the relative path from an ancestor. This method attempts to perform the reverse
107      * of concatenating a base (ancestor) and a path.
108      *
109      * @param ancestor Ancestor against which the relative path should be calculated
110      * @return This object's relative path from parent, or Optional.absent() if the
111      *         specified parent is not in fact an ancestor of this object.
112      */
113     public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
114         if (ancestor.contains(this)) {
115             final int common = ancestor.path.size();
116             return Optional.of(new InstanceIdentifier(path.subList(common, path.size())));
117         } else {
118             return Optional.absent();
119         }
120     }
121
122     // Static factories & helpers
123
124     public static InstanceIdentifier of(final QName name) {
125         return new InstanceIdentifier(new NodeIdentifier(name));
126     }
127
128     static public InstanceIdentifierBuilder builder() {
129         return new BuilderImpl();
130     }
131
132     static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
133         return new BuilderImpl(origin.getPath());
134     }
135
136     public static InstanceIdentifierBuilder builder(final QName node) {
137         return builder().node(node);
138     }
139
140     public interface PathArgument extends Immutable, Serializable {
141
142         /**
143          * If applicable returns uniqee QName of data node as defined in YANG
144          * Schema.
145          *
146          * This method may return null, if the corresponding schema node, does
147          * not have QName associated, such as in cases of augmentations.
148          *
149          * @return
150          */
151         QName getNodeType();
152
153     }
154
155     public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
156         InstanceIdentifierBuilder node(QName nodeType);
157
158         InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
159
160         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
161
162         @Deprecated
163         InstanceIdentifier getIdentifier();
164
165         InstanceIdentifier build();
166     }
167
168     /**
169      * Simple path argument identifying a {@link ContainerNode} or {@link LeafNode} leaf
170      * overal data tree.
171      *
172      */
173     public static final class NodeIdentifier implements PathArgument {
174
175         /**
176          *
177          */
178         private static final long serialVersionUID = -2255888212390871347L;
179
180         private final QName nodeType;
181
182         public NodeIdentifier(final QName node) {
183             this.nodeType = node;
184         }
185
186         @Override
187         public QName getNodeType() {
188             return nodeType;
189         }
190
191         @Override
192         public int hashCode() {
193             final int prime = 31;
194             int result = 1;
195             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
196             return result;
197         }
198
199         @Override
200         public boolean equals(final Object obj) {
201             if (this == obj) {
202                                 return true;
203                         }
204             if (obj == null) {
205                                 return false;
206                         }
207             if (getClass() != obj.getClass()) {
208                                 return false;
209                         }
210             NodeIdentifier other = (NodeIdentifier) obj;
211             if (nodeType == null) {
212                 if (other.nodeType != null) {
213                                         return false;
214                                 }
215             } else if (!nodeType.equals(other.nodeType)) {
216                                 return false;
217                         }
218             return true;
219         }
220
221         @Override
222         public String toString() {
223             return nodeType.toString();
224         }
225     }
226
227     /**
228      *
229      * Composite path argument identifying a {@link MapEntryNode} leaf
230      * overal data tree.
231      *
232      */
233     public static final class NodeIdentifierWithPredicates implements PathArgument {
234
235         /**
236          *
237          */
238         private static final long serialVersionUID = -4787195606494761540L;
239
240         private final QName nodeType;
241         private final Map<QName, Object> keyValues;
242
243         public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
244             this.nodeType = node;
245             this.keyValues = ImmutableMap.copyOf(keyValues);
246         }
247
248         public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
249             this.nodeType = node;
250             this.keyValues = ImmutableMap.of(key, value);
251         }
252
253         @Override
254         public QName getNodeType() {
255             return nodeType;
256         }
257
258         public Map<QName, Object> getKeyValues() {
259             return keyValues;
260         }
261
262         @Override
263         public int hashCode() {
264             final int prime = 31;
265             int result = 1;
266             result = prime * result + ((keyValues == null) ? 0 : keyValues.hashCode());
267             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
268             return result;
269         }
270
271         @Override
272         public boolean equals(final Object obj) {
273             if (this == obj) {
274                                 return true;
275                         }
276             if (obj == null) {
277                                 return false;
278                         }
279             if (getClass() != obj.getClass()) {
280                                 return false;
281                         }
282             NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
283             if (keyValues == null) {
284                 if (other.keyValues != null) {
285                                         return false;
286                                 }
287             } else if (!keyValues.equals(other.keyValues)) {
288                                 return false;
289                         }
290             if (nodeType == null) {
291                 if (other.nodeType != null) {
292                                         return false;
293                                 }
294             } else if (!nodeType.equals(other.nodeType)) {
295                                 return false;
296                         }
297             return true;
298         }
299
300         @Override
301         public String toString() {
302             return nodeType + "[" + keyValues + "]";
303         }
304     }
305
306     /**
307      * Simple path argument identifying a {@link LeafSetEntryNode} leaf
308      * overal data tree.
309      *
310      */
311     public static final class NodeWithValue implements PathArgument {
312
313        /**
314         *
315         * Composite path argument identifying a {@link AugmentationNode} leaf
316         * overal data tree.
317         *
318         */
319         private static final long serialVersionUID = -3637456085341738431L;
320
321         private final QName nodeType;
322         private final Object value;
323
324         public NodeWithValue(final QName node, final Object value) {
325             this.nodeType = node;
326             this.value = value;
327         }
328
329         @Override
330         public QName getNodeType() {
331             return nodeType;
332         }
333
334         public Object getValue() {
335             return value;
336         }
337
338         @Override
339         public int hashCode() {
340             final int prime = 31;
341             int result = 1;
342             result = prime * result + ((value == null) ? 0 : value.hashCode());
343             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
344             return result;
345         }
346
347         @Override
348         public boolean equals(final Object obj) {
349             if (this == obj) {
350                                 return true;
351                         }
352             if (obj == null) {
353                                 return false;
354                         }
355             if (getClass() != obj.getClass()) {
356                                 return false;
357                         }
358             NodeWithValue other = (NodeWithValue) obj;
359             if (value == null) {
360                 if (other.value != null) {
361                                         return false;
362                                 }
363             } else if (!value.equals(other.value)) {
364                                 return false;
365                         }
366             if (nodeType == null) {
367                 if (other.nodeType != null) {
368                                         return false;
369                                 }
370             } else if (!nodeType.equals(other.nodeType)) {
371                                 return false;
372                         }
373             return true;
374         }
375
376         @Override
377         public String toString() {
378             return nodeType + "[" + value + "]";
379         }
380
381     }
382
383
384     public static final class AugmentationIdentifier implements PathArgument {
385
386         private static final long serialVersionUID = -8122335594681936939L;
387         private final ImmutableSet<QName> childNames;
388
389         @Override
390         public QName getNodeType() {
391             // This should rather throw exception than return always null
392             throw new UnsupportedOperationException("Augmentation node has no QName");
393         }
394
395         public AugmentationIdentifier(final Set<QName> childNames) {
396             this.childNames = ImmutableSet.copyOf(childNames);
397         }
398
399         /**
400          * Augmentation node has no QName
401          */
402         @Deprecated
403         public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
404             this(childNames);
405         }
406
407         public Set<QName> getPossibleChildNames() {
408             return childNames;
409         }
410
411         @Override
412         public String toString() {
413             final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
414             sb.append("childNames=").append(childNames);
415             sb.append('}');
416             return sb.toString();
417         }
418
419         @Override
420         public boolean equals(final Object o) {
421             if (this == o) {
422                                 return true;
423                         }
424             if (!(o instanceof AugmentationIdentifier)) {
425                                 return false;
426                         }
427
428             AugmentationIdentifier that = (AugmentationIdentifier) o;
429
430             if (!childNames.equals(that.childNames)) {
431                                 return false;
432                         }
433
434             return true;
435         }
436
437         @Override
438         public int hashCode() {
439             return childNames.hashCode();
440         }
441     }
442
443     private static class BuilderImpl implements InstanceIdentifierBuilder {
444
445         private final ImmutableList.Builder<PathArgument> path;
446
447         public BuilderImpl() {
448             path = ImmutableList.<PathArgument> builder();
449         }
450
451         public BuilderImpl(final List<? extends PathArgument> prefix) {
452             path = ImmutableList.<PathArgument> builder();
453             path.addAll(prefix);
454         }
455
456         @Override
457         public InstanceIdentifierBuilder node(final QName nodeType) {
458             path.add(new NodeIdentifier(nodeType));
459             return this;
460         }
461
462         @Override
463         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
464             path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
465             return this;
466         }
467
468         @Override
469         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
470             path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
471             return this;
472         }
473
474         @Override
475         @Deprecated
476         public InstanceIdentifier toInstance() {
477             return build();
478         }
479
480         @Override
481         public InstanceIdentifier build() {
482             return new InstanceIdentifier(path.build());
483         }
484
485         @Override
486         @Deprecated
487         public InstanceIdentifier getIdentifier() {
488             return build();
489         }
490     }
491
492     @Override
493     public boolean contains(final InstanceIdentifier other) {
494         if (other == null) {
495             throw new IllegalArgumentException("other should not be null");
496         }
497         final int localSize = this.path.size();
498         final List<PathArgument> otherPath = other.getPath();
499         if (localSize > other.path.size()) {
500             return false;
501         }
502         for (int i = 0; i < localSize; i++) {
503             if (!path.get(i).equals(otherPath.get(i))) {
504                 return false;
505             }
506         }
507         return true;
508     }
509
510     @Override
511     public String toString() {
512         /*
513          * The toStringCache is safe, since the object contract requires
514          * immutability of the object and all objects referenced from this
515          * object.
516          *
517          * Used lists, maps are immutable. Path Arguments (elements) are also
518          * immutable, since the PathArgument contract requires immutability.
519          *
520          * The cache is thread-safe - if multiple computations occurs at the
521          * same time, cache will be overwritten with same result.
522          */
523         if (toStringCache != null) {
524             return toStringCache;
525         }
526
527         final StringBuilder builder = new StringBuilder('/');
528         boolean first = true;
529         for (PathArgument argument : path) {
530             if (first) {
531                 first = false;
532             } else {
533                 builder.append('/');
534             }
535             builder.append(argument.toString());
536         }
537
538         toStringCache = builder.toString();
539         return toStringCache;
540     }
541 }