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