Merge "BUG-994: remove the legacy public constructor"
[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  * This program and the accompanying materials are made available under the
4  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
5  * and is available at http://www.eclipse.org/legal/epl-v10.html
6  */
7 package org.opendaylight.yangtools.yang.data.api;
8
9 import com.google.common.base.Optional;
10 import com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.ImmutableMap;
13 import com.google.common.collect.ImmutableSet;
14 import java.io.Serializable;
15 import java.lang.reflect.Array;
16 import java.util.Arrays;
17 import java.util.Iterator;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Map.Entry;
21 import java.util.Objects;
22 import java.util.Set;
23 import org.opendaylight.yangtools.concepts.Builder;
24 import org.opendaylight.yangtools.concepts.Immutable;
25 import org.opendaylight.yangtools.concepts.Path;
26 import org.opendaylight.yangtools.yang.common.QName;
27 import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
28
29 /**
30  * Unique identifier of a partical node instance in the data tree.
31  *
32  *
33  * <p>
34  * Java representation of YANG Built-in type <code>instance-identifier</code>,
35  * which conceptually is XPath expression minimised to uniquely identify element
36  * in data tree which conforms to constraints maintained by YANG Model,
37  * effectively this makes Instance Identifier a path to element in data tree.
38  * <p>
39  * Constraints put in YANG specification on instance-identifier allowed it to be
40  * effectively represented in Java and it's evaluation does not require
41  * full-blown XPath processor.
42  * <p>
43  * <h3>Path Arguments</h3>
44  * Path to the node represented in instance identifier consists of
45  * {@link PathArgument} which carries necessary information to uniquely identify
46  * node on particular level in the subtree.
47  * <p>
48  * <ul>
49  * <li>{@link NodeIdentifier} - Identifier of node, which has cardinality
50  * <code>0..1</code> in particular subtree in data tree.</li>
51  * <li>{@link NodeIdentifierWithPredicates} - Identifier of node (list item),
52  * which has cardinality <code>0..n</code>.</li>
53  * <li>{@link NodeWithValue} - Identifier of instance <code>leaf</code> node or
54  * <code>leaf-list</code> node.</li>
55  * <li>{@link AugmentationIdentifier} - Identifier of instance of
56  * <code>augmentation</code> node.</li>
57  * </ul>
58  *
59  *
60  * @see http://tools.ietf.org/html/rfc6020#section-9.13
61  *
62  *
63  */
64 public class InstanceIdentifier implements Path<InstanceIdentifier>, Immutable, Serializable {
65
66     private static final long serialVersionUID = 8467409862384206193L;
67     private final List<PathArgument> path;
68
69     private transient String toStringCache = null;
70     private transient Integer hashCodeCache = null;
71
72     /**
73      *
74      * Returns a list of path arguments.
75      *
76      * @deprecated Use {@link #getPathArguments()} instead.
77      * @return Immutable list of path arguments.
78      */
79     @Deprecated
80     public List<PathArgument> getPath() {
81         return path;
82     }
83
84     /**
85      *
86      * Returns a ordered iteration of path arguments.
87      *
88      * @return Immutable iteration of path arguments.
89      */
90     public Iterable<PathArgument> getPathArguments() {
91         return path;
92     }
93
94     /**
95      *
96      *
97      * @deprecated Use {@link #create(Iterable)} instead.
98      * @param path
99      */
100     @Deprecated
101     public InstanceIdentifier(final List<? extends PathArgument> path) {
102         this.path = ImmutableList.copyOf(path);
103     }
104
105     private InstanceIdentifier(final Iterable<? extends PathArgument> path) {
106         Preconditions.checkNotNull(path, "path must not be null.");
107         this.path = ImmutableList.copyOf(path);
108     }
109
110     private InstanceIdentifier(final NodeIdentifier nodeIdentifier) {
111         this.path = ImmutableList.<PathArgument> of(nodeIdentifier);
112     }
113
114     public static final InstanceIdentifier create(final Iterable<? extends PathArgument> path) {
115         return new InstanceIdentifier(path);
116     }
117
118     @Override
119     public int hashCode() {
120         /*
121          * The hashCodeCache is safe, since the object contract requires
122          * immutability of the object and all objects referenced from this
123          * object.
124          * Used lists, maps are immutable. Path Arguments (elements) are also
125          * immutable, since the PathArgument contract requires immutability.
126          * The cache is thread-safe - if multiple computations occurs at the
127          * same time, cache will be overwritten with same result.
128          */
129         if (hashCodeCache == null) {
130             final int prime = 31;
131             int result = 1;
132             result = prime * result + path.hashCode();
133             hashCodeCache = result;
134         }
135         return hashCodeCache;
136     }
137
138     @Override
139     public boolean equals(final Object obj) {
140         if (this == obj) {
141             return true;
142         }
143         if (obj == null) {
144             return false;
145         }
146         if (getClass() != obj.getClass()) {
147             return false;
148         }
149         InstanceIdentifier other = (InstanceIdentifier) obj;
150         if (this.hashCode() != obj.hashCode()) {
151             return false;
152         }
153         if (path == null) {
154             if (other.path != null) {
155                 return false;
156             }
157         } else if (!path.equals(other.path)) {
158             return false;
159         }
160         return true;
161     }
162
163     /**
164      *
165      * Constructs a new Instance Identifier with new {@link NodeIdentifier} added to the end of path arguments
166      *
167      * @param name QName of {@link NodeIdentifier}
168      * @return Instance Identifier with additional path argument added to the end.
169      */
170     public InstanceIdentifier node(final QName name) {
171         return node(new NodeIdentifier(name));
172     }
173
174     /**
175      *
176      * Constructs a new Instance Identifier with new {@link PathArgument} added to the end of path arguments
177      *
178      * @param arg Path argument which should be added to the end
179      * @return Instance Identifier with additional path argument added to the end.
180      */
181     public InstanceIdentifier node(final PathArgument arg) {
182         return create(ImmutableList.<PathArgument> builder().addAll(path).add(arg).build());
183     }
184
185     /**
186      * Get the relative path from an ancestor. This method attempts to perform
187      * the reverse
188      * of concatenating a base (ancestor) and a path.
189      *
190      * @param ancestor
191      *            Ancestor against which the relative path should be calculated
192      * @return This object's relative path from parent, or Optional.absent() if
193      *         the
194      *         specified parent is not in fact an ancestor of this object.
195      */
196     public Optional<InstanceIdentifier> relativeTo(final InstanceIdentifier ancestor) {
197         if (ancestor.contains(this)) {
198             final int common = ancestor.path.size();
199             return Optional.of(new InstanceIdentifier(path.subList(common, path.size())));
200         } else {
201             return Optional.absent();
202         }
203     }
204
205     static int hashCode(final Object value) {
206         if (value == null) {
207             return 0;
208         }
209
210         if (value.getClass().equals(byte[].class)) {
211             return Arrays.hashCode((byte[]) value);
212         }
213
214         if (value.getClass().isArray()) {
215             int hash = 0;
216             int length = Array.getLength(value);
217             for (int i = 0; i < length; i++) {
218                 hash += Objects.hashCode(Array.get(value, i));
219             }
220
221             return hash;
222         }
223
224         return Objects.hashCode(value);
225     }
226
227     // Static factories & helpers
228
229     /**
230      *
231      * Returns a new InstanceIdentifier with only one path argument of type {@link NodeIdentifier} with supplied QName
232      *
233      * @param name QName of first node identifier
234      * @return Instance Identifier with only one path argument of type {@link NodeIdentifier}
235      */
236     public static InstanceIdentifier of(final QName name) {
237         return new InstanceIdentifier(new NodeIdentifier(name));
238     }
239
240     /**
241      *
242      * Returns new builder for InstanceIdentifier with empty path arguments.
243      *
244      * @return new builder for InstanceIdentifier with empty path arguments.
245      */
246     static public InstanceIdentifierBuilder builder() {
247         return new BuilderImpl();
248     }
249
250     /**
251     *
252     * Returns new builder for InstanceIdentifier with path arguments copied from original instance identifier.
253     *
254     * @param origin Instace Identifier from which path arguments are copied.
255     * @return new builder for InstanceIdentifier with path arguments copied from original instance identifier.
256     */
257     static public InstanceIdentifierBuilder builder(final InstanceIdentifier origin) {
258         return new BuilderImpl(origin.getPath());
259     }
260    /**
261     *
262     * Returns new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
263     *
264     * @param node QName of first {@link NodeIdentifier} path argument.
265     * @return  new builder for InstanceIdentifier with first path argument set to {@link NodeIdentifier}.
266     */
267     public static InstanceIdentifierBuilder builder(final QName node) {
268         return builder().node(node);
269     }
270
271     /**
272      *
273      * Path argument / component of InstanceIdentifier
274      *
275      * Path argument uniquelly identifies node in data tree on particular
276      * level.
277      * <p>
278      * This interface itself is used as common parent for actual
279      * path arguments types and should not be implemented by user code.
280      * <p>
281      * Path arguments SHOULD contain only minimum of information
282      * required to uniquely identify node on particular subtree level.
283      *
284      * For actual path arguments types see:
285      * <ul>
286      * <li>{@link NodeIdentifier} - Identifier of container or leaf
287      * <li>{@link NodeIdentifierWithPredicates} - Identifier of list entries, which have key defined
288      * <li>{@link AugmentationIdentifier} - Identifier of augmentation
289      * <li>{@link NodeWithValue} - Identifier of leaf-list entry
290      * </ul>
291      *
292      *
293      *
294      */
295     public interface PathArgument extends Comparable<PathArgument>, Immutable, Serializable {
296
297         /**
298          * If applicable returns unique QName of data node as defined in YANG
299          * Schema.
300          *
301          * This method may return null, if the corresponding schema node, does
302          * not have QName associated, such as in cases of augmentations.
303          *
304          * @return
305          */
306         QName getNodeType();
307
308     }
309
310     private static abstract class AbstractPathArgument implements PathArgument {
311         private static final long serialVersionUID = -4546547994250849340L;
312         protected final QName nodeType;
313
314         protected AbstractPathArgument(final QName nodeType) {
315             this.nodeType = Preconditions.checkNotNull(nodeType);
316         }
317
318         @Override
319         public QName getNodeType() {
320             return nodeType;
321         }
322
323         @Override
324         public int compareTo(final PathArgument o) {
325             return nodeType.compareTo(o.getNodeType());
326         }
327
328     }
329
330     /**
331      *
332      * Fluent Builder of Instance Identifier instances
333      *
334      * @
335      *
336      */
337     public interface InstanceIdentifierBuilder extends Builder<InstanceIdentifier> {
338
339         /**
340          *
341          * Adds {@link NodeIdentifier} with supplied QName to path arguments of resulting instance identifier.
342          *
343          * @param nodeType QName of {@link NodeIdentifier} which will be added
344          * @return this builder
345          */
346         InstanceIdentifierBuilder node(QName nodeType);
347
348         /**
349          *
350          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key values to path arguments of resulting instance identifier.
351          *
352          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
353          * @param keyValues Map of key components and their respective values for {@link NodeIdentifierWithPredicates}
354          * @return this builder
355          */
356         InstanceIdentifierBuilder nodeWithKey(QName nodeType, Map<QName, Object> keyValues);
357
358         /**
359          *
360          * Adds {@link NodeIdentifierWithPredicates} with supplied QName and key, value.
361          *
362          * @param nodeType QName of {@link NodeIdentifierWithPredicates} which will be added
363          * @param key QName of key which will be added
364          * @param value value of key which will be added
365          * @return this builder
366          */
367         InstanceIdentifierBuilder nodeWithKey(QName nodeType, QName key, Object value);
368
369         /**
370          *
371          * @return
372          * @deprecated use {@link #build()}
373          *
374          */
375         @Deprecated
376         InstanceIdentifier getIdentifier();
377
378         /**
379          *
380          * Builds an {@link InstanceIdentifier} with path arguments from this builder
381          *
382          * @return {@link InstanceIdentifier}
383          */
384         InstanceIdentifier build();
385     }
386
387     /**
388      * Simple path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.ContainerNode} or
389      * {@link org.opendaylight.yangtools.yang.data.api.schema.LeafNode} leaf in particular subtree.
390      */
391     public static final class NodeIdentifier extends AbstractPathArgument {
392         private static final long serialVersionUID = -2255888212390871347L;
393
394         public NodeIdentifier(final QName node) {
395             super(node);
396         }
397
398         @Override
399         public int hashCode() {
400             return 31 + nodeType.hashCode();
401         }
402
403         @Override
404         public boolean equals(final Object obj) {
405             if (this == obj) {
406                 return true;
407             }
408             if (!(obj instanceof NodeIdentifier)) {
409                 return false;
410             }
411             final NodeIdentifier other = (NodeIdentifier) obj;
412             return nodeType.equals(other.nodeType);
413         }
414
415         @Override
416         public String toString() {
417             return nodeType.toString();
418         }
419
420     }
421
422     /**
423      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode} leaf
424      * overall data tree.
425      */
426     public static final class NodeIdentifierWithPredicates extends AbstractPathArgument {
427         private static final long serialVersionUID = -4787195606494761540L;
428
429         private final Map<QName, Object> keyValues;
430
431         public NodeIdentifierWithPredicates(final QName node, final Map<QName, Object> keyValues) {
432             super(node);
433             this.keyValues = ImmutableMap.copyOf(keyValues);
434         }
435
436         public NodeIdentifierWithPredicates(final QName node, final QName key, final Object value) {
437             this(node, ImmutableMap.of(key, value));
438         }
439
440         @Override
441         public QName getNodeType() {
442             return nodeType;
443         }
444
445         public Map<QName, Object> getKeyValues() {
446             return keyValues;
447         }
448
449         @Override
450         public int hashCode() {
451             final int prime = 31;
452             int result = 1;
453             result = prime * result + ((keyValues == null) ? 0 : hashKeyValues());
454             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
455             return result;
456         }
457
458         private int hashKeyValues() {
459             int hash = 0;
460             for (Entry<QName, Object> entry : keyValues.entrySet()) {
461                 hash += Objects.hashCode(entry.getKey()) + InstanceIdentifier.hashCode(entry.getValue());
462             }
463
464             return hash;
465         }
466
467         @Override
468         public boolean equals(final Object obj) {
469             if (this == obj) {
470                 return true;
471             }
472             if (obj == null) {
473                 return false;
474             }
475             if (getClass() != obj.getClass()) {
476                 return false;
477             }
478             NodeIdentifierWithPredicates other = (NodeIdentifierWithPredicates) obj;
479             if (keyValues == null) {
480                 if (other.keyValues != null) {
481                     return false;
482                 }
483             } else if (!keyValuesEquals(other.keyValues)) {
484                 return false;
485             }
486             if (nodeType == null) {
487                 if (other.nodeType != null) {
488                     return false;
489                 }
490             } else if (!nodeType.equals(other.nodeType)) {
491                 return false;
492             }
493             return true;
494         }
495
496         private boolean keyValuesEquals(final Map<QName, Object> otherKeyValues) {
497             if (otherKeyValues == null || keyValues.size() != otherKeyValues.size()) {
498                 return false;
499             }
500
501             boolean result = true;
502             for (Entry<QName, Object> entry : keyValues.entrySet()) {
503                 if (!otherKeyValues.containsKey(entry.getKey())
504                         || !Objects.deepEquals(entry.getValue(), otherKeyValues.get(entry.getKey()))) {
505
506                     result = false;
507                     break;
508                 }
509             }
510
511             return result;
512         }
513
514         @Override
515         public String toString() {
516             return nodeType + "[" + keyValues + "]";
517         }
518     }
519
520     /**
521      * Simple path argument identifying a {@link LeafSetEntryNode} leaf
522      * overall data tree.
523      */
524     public static final class NodeWithValue extends AbstractPathArgument {
525         private static final long serialVersionUID = -3637456085341738431L;
526
527         private final Object value;
528
529         public NodeWithValue(final QName node, final Object value) {
530             super(node);
531             this.value = value;
532         }
533
534         @Override
535         public QName getNodeType() {
536             return nodeType;
537         }
538
539         public Object getValue() {
540             return value;
541         }
542
543         @Override
544         public int hashCode() {
545             final int prime = 31;
546             int result = 1;
547             result = prime * result + ((value == null) ? 0 : InstanceIdentifier.hashCode(value));
548             result = prime * result + ((nodeType == null) ? 0 : nodeType.hashCode());
549             return result;
550         }
551
552         @Override
553         public boolean equals(final Object obj) {
554             if (this == obj) {
555                 return true;
556             }
557             if (obj == null) {
558                 return false;
559             }
560             if (getClass() != obj.getClass()) {
561                 return false;
562             }
563             NodeWithValue other = (NodeWithValue) obj;
564             return Objects.deepEquals(value, other.value) && Objects.equals(nodeType, other.nodeType);
565         }
566
567         @Override
568         public String toString() {
569             return nodeType + "[" + value + "]";
570         }
571
572     }
573
574     /**
575      * Composite path argument identifying a {@link org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode} node in
576      * particular subtree.
577      *
578      * Augmentation is uniquely identified by set of all possible child nodes.
579      * This is possible
580      * to identify instance of augmentation,
581      * since RFC6020 states that <code>augment</code> that augment
582      * statement must not add multiple nodes from same namespace
583      * / module to the target node.
584      *
585      *
586      * @see http://tools.ietf.org/html/rfc6020#section-7.15
587      */
588     public static final class AugmentationIdentifier implements PathArgument {
589         private static final long serialVersionUID = -8122335594681936939L;
590         private final ImmutableSet<QName> childNames;
591
592         @Override
593         public QName getNodeType() {
594             // This should rather throw exception than return always null
595             throw new UnsupportedOperationException("Augmentation node has no QName");
596         }
597
598         /**
599          *
600          * Construct new augmentation identifier using supplied set of possible
601          * child nodes
602          *
603          * @param childNames
604          *            Set of possible child nodes.
605          */
606         public AugmentationIdentifier(final Set<QName> childNames) {
607             this.childNames = ImmutableSet.copyOf(childNames);
608         }
609
610         /**
611          * Augmentation node has no QName
612          *
613          * @deprecated Use
614          *             {@link AugmentationIdentifier#AugmentationIdentifier(Set)}
615          *             instead.
616          */
617         @Deprecated
618         public AugmentationIdentifier(final QName nodeType, final Set<QName> childNames) {
619             this(childNames);
620         }
621
622         /**
623          *
624          * Returns set of all possible child nodes
625          *
626          * @return set of all possible child nodes.
627          */
628         public Set<QName> getPossibleChildNames() {
629             return childNames;
630         }
631
632         @Override
633         public String toString() {
634             final StringBuffer sb = new StringBuffer("AugmentationIdentifier{");
635             sb.append("childNames=").append(childNames);
636             sb.append('}');
637             return sb.toString();
638         }
639
640         @Override
641         public boolean equals(final Object o) {
642             if (this == o) {
643                 return true;
644             }
645             if (!(o instanceof AugmentationIdentifier)) {
646                 return false;
647             }
648
649             AugmentationIdentifier that = (AugmentationIdentifier) o;
650
651             if (!childNames.equals(that.childNames)) {
652                 return false;
653             }
654
655             return true;
656         }
657
658         @Override
659         public int hashCode() {
660             return childNames.hashCode();
661         }
662
663         @Override
664         public int compareTo(PathArgument o) {
665             if (!(o instanceof AugmentationIdentifier)) {
666                 return -1;
667             }
668             AugmentationIdentifier other = (AugmentationIdentifier) o;
669             Set<QName> otherChildNames = other.getPossibleChildNames();
670             int thisSize = childNames.size();
671             int otherSize = otherChildNames.size();
672             if (thisSize == otherSize) {
673                 Iterator<QName> otherIterator = otherChildNames.iterator();
674                 for (QName name : childNames) {
675                     int c = name.compareTo(otherIterator.next());
676                     if (c != 0) {
677                         return c;
678                     }
679                 }
680                 return 0;
681             } else if (thisSize < otherSize) {
682                 return 1;
683             } else {
684                 return -1;
685             }
686         }
687     }
688
689     private static class BuilderImpl implements InstanceIdentifierBuilder {
690
691         private final ImmutableList.Builder<PathArgument> path;
692
693         public BuilderImpl() {
694             path = ImmutableList.<PathArgument> builder();
695         }
696
697         public BuilderImpl(final List<? extends PathArgument> prefix) {
698             path = ImmutableList.<PathArgument> builder();
699             path.addAll(prefix);
700         }
701
702         @Override
703         public InstanceIdentifierBuilder node(final QName nodeType) {
704             path.add(new NodeIdentifier(nodeType));
705             return this;
706         }
707
708         @Override
709         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final QName key, final Object value) {
710             path.add(new NodeIdentifierWithPredicates(nodeType, key, value));
711             return this;
712         }
713
714         @Override
715         public InstanceIdentifierBuilder nodeWithKey(final QName nodeType, final Map<QName, Object> keyValues) {
716             path.add(new NodeIdentifierWithPredicates(nodeType, keyValues));
717             return this;
718         }
719
720         @Override
721         @Deprecated
722         public InstanceIdentifier toInstance() {
723             return build();
724         }
725
726         @Override
727         public InstanceIdentifier build() {
728             return new InstanceIdentifier(path.build());
729         }
730
731         @Override
732         @Deprecated
733         public InstanceIdentifier getIdentifier() {
734             return build();
735         }
736     }
737
738     @Override
739     public boolean contains(final InstanceIdentifier other) {
740         if (other == null) {
741             throw new IllegalArgumentException("other should not be null");
742         }
743         final int localSize = this.path.size();
744         final List<PathArgument> otherPath = other.getPath();
745         if (localSize > other.path.size()) {
746             return false;
747         }
748         for (int i = 0; i < localSize; i++) {
749             if (!path.get(i).equals(otherPath.get(i))) {
750                 return false;
751             }
752         }
753         return true;
754     }
755
756     @Override
757     public String toString() {
758         /*
759          * The toStringCache is safe, since the object contract requires
760          * immutability of the object and all objects referenced from this
761          * object.
762          * Used lists, maps are immutable. Path Arguments (elements) are also
763          * immutable, since the PathArgument contract requires immutability.
764          * The cache is thread-safe - if multiple computations occurs at the
765          * same time, cache will be overwritten with same result.
766          */
767         if (toStringCache != null) {
768             return toStringCache;
769         }
770
771         final StringBuilder builder = new StringBuilder('/');
772         boolean first = true;
773         for (PathArgument argument : path) {
774             if (first) {
775                 first = false;
776             } else {
777                 builder.append('/');
778             }
779             builder.append(argument.toString());
780         }
781
782         toStringCache = builder.toString();
783         return toStringCache;
784     }
785 }