Cleanup use of Guava library
[yangtools.git] / yang / yang-data-impl / src / main / java / org / opendaylight / yangtools / yang / data / impl / leafref / LeafRefPath.java
1 /*
2  * Copyright (c) 2015 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.impl.leafref;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.Iterables;
14 import java.util.Arrays;
15 import java.util.Iterator;
16 import java.util.NoSuchElementException;
17 import java.util.Objects;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
19 import org.opendaylight.yangtools.concepts.Immutable;
20
21 public abstract class LeafRefPath implements Immutable {
22
23     /**
24      * An absolute LeafRefPath.
25      */
26     private static final class AbsoluteLeafRefPath extends LeafRefPath {
27         private AbsoluteLeafRefPath(final LeafRefPath parent,
28                 final QNameWithPredicate qname) {
29             super(parent, qname);
30         }
31
32         @Override
33         public boolean isAbsolute() {
34             return true;
35         }
36
37         @Override
38         protected LeafRefPath createInstance(final LeafRefPath parent,
39                 final QNameWithPredicate qname) {
40             return new AbsoluteLeafRefPath(parent, qname);
41         }
42     }
43
44     /**
45      * A relative LeafRefPath.
46      */
47     private static final class RelativeLeafRefPath extends LeafRefPath {
48         private RelativeLeafRefPath(final LeafRefPath parent,
49                 final QNameWithPredicate qname) {
50             super(parent, qname);
51         }
52
53         @Override
54         public boolean isAbsolute() {
55             return false;
56         }
57
58         @Override
59         protected LeafRefPath createInstance(final LeafRefPath parent,
60                 final QNameWithPredicate qname) {
61             return new RelativeLeafRefPath(parent, qname);
62         }
63     }
64
65     @SuppressWarnings("rawtypes")
66     private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER =
67         AtomicReferenceFieldUpdater.newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
68
69     /**
70      * Shared instance of the conceptual root schema node.
71      */
72     public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
73
74     /**
75      * Shared instance of the "same" relative schema node.
76      */
77     public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
78
79     /**
80      * Parent path.
81      */
82     private final LeafRefPath parent;
83
84     /**
85      * This component.
86      */
87     private final QNameWithPredicate qname;
88
89     /**
90      * Cached hash code. We can use this since we are immutable.
91      */
92     private final int hash;
93
94     /**
95      * Cached legacy path, filled-in when {@link #getPath()} or
96      * {@link #getPathTowardsRoot()} is invoked.
97      */
98     private volatile ImmutableList<QNameWithPredicate> legacyPath;
99
100     private ImmutableList<QNameWithPredicate> getLegacyPath() {
101         ImmutableList<QNameWithPredicate> ret = legacyPath;
102         if (ret == null) {
103             ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
104             LEGACYPATH_UPDATER.lazySet(this, ret);
105         }
106
107         return ret;
108     }
109
110     protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
111         this.parent = parent;
112         this.qname = qname;
113
114         int hc = Objects.hashCode(parent);
115         if (qname != null) {
116             hc = hc * 31 + qname.hashCode();
117         }
118
119         hash = hc;
120     }
121
122     /**
123      * Constructs new instance of this class with the concrete path.
124      *
125      * @param path
126      *            list of QNameWithPredicate instances which specifies exact
127      *            path to the module node
128      * @param absolute
129      *            boolean value which specifies if the path is absolute or
130      *            relative
131      *
132      * @return A LeafRefPath instance.
133      */
134     public static LeafRefPath create(final Iterable<QNameWithPredicate> path,
135             final boolean absolute) {
136         final LeafRefPath parent = absolute ? ROOT : SAME;
137         return parent.createChild(path);
138     }
139
140     /**
141      * Constructs new instance of this class with the concrete path.
142      *
143      * @param absolute
144      *            boolean value which specifies if the path is absolute or
145      *            relative
146      * @param path
147      *            one or more QNameWithPredicate instances which specifies exact
148      *            path to the module node
149      *
150      * @return A LeafRefPath instance.
151      */
152     public static LeafRefPath create(final boolean absolute,
153             final QNameWithPredicate... path) {
154         return create(Arrays.asList(path), absolute);
155     }
156
157     /**
158      * Create a new instance.
159      *
160      * @param parent
161      *            Parent LeafRefPath
162      * @param qname
163      *            next path element
164      * @return A new LeafRefPath instance
165      */
166     protected abstract LeafRefPath createInstance(LeafRefPath parent,
167             QNameWithPredicate qname);
168
169     /**
170      * Create a child path based on concatenation of this path and a relative
171      * path.
172      *
173      * @param relative
174      *            Relative path
175      * @return A new child path
176      */
177     public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
178         if (Iterables.isEmpty(relative)) {
179             return this;
180         }
181
182         LeafRefPath parent = this;
183         for (QNameWithPredicate qname : relative) {
184             parent = parent.createInstance(parent, qname);
185         }
186
187         return parent;
188     }
189
190     /**
191      * Create a child path based on concatenation of this path and a relative
192      * path.
193      *
194      * @param relative
195      *            Relative LeafRefPath
196      * @return A new child path
197      */
198     public LeafRefPath createChild(final LeafRefPath relative) {
199         checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
200
201         LeafRefPath parent = this;
202         for (QNameWithPredicate qname : relative.getPathFromRoot()) {
203             parent = parent.createInstance(parent, qname);
204         }
205
206         return parent;
207     }
208
209     /**
210      * Create a child path based on concatenation of this path and additional
211      * path elements.
212      *
213      * @param elements
214      *            Relative LeafRefPath elements
215      * @return A new child path
216      */
217     public LeafRefPath createChild(final QNameWithPredicate... elements) {
218         return createChild(Arrays.asList(elements));
219     }
220
221     /**
222      * Returns the list of nodes which need to be traversed to get from the
223      * starting point (root for absolute LeafRefPaths) to the node represented
224      * by this object.
225      *
226      * @return list of <code>qname</code> instances which represents path from
227      *         the root to the schema node.
228      */
229     public Iterable<QNameWithPredicate> getPathFromRoot() {
230         return getLegacyPath();
231     }
232
233     /**
234      * Returns the list of nodes which need to be traversed to get from this
235      * node to the starting point (root for absolute LeafRefPaths).
236      *
237      * @return list of <code>qname</code> instances which represents path from
238      *         the schema node towards the root.
239      */
240     public Iterable<QNameWithPredicate> getPathTowardsRoot() {
241         return () -> new Iterator<QNameWithPredicate>() {
242             private LeafRefPath current = LeafRefPath.this;
243
244             @Override
245             public boolean hasNext() {
246                 return current.parent != null;
247             }
248
249             @Override
250             public QNameWithPredicate next() {
251                 if (current.parent == null) {
252                     throw new NoSuchElementException("No more elements available");
253                 }
254
255                 final QNameWithPredicate ret = current.qname;
256                 current = current.parent;
257                 return ret;
258             }
259
260             @Override
261             public void remove() {
262                 throw new UnsupportedOperationException("Component removal not supported");
263             }
264         };
265     }
266
267     /**
268      * Returns the immediate parent LeafRefPath.
269      *
270      * @return Parent path, null if this LeafRefPath is already toplevel.
271      */
272     public LeafRefPath getParent() {
273         return parent;
274     }
275
276     /**
277      * Get the last component of this path.
278      *
279      * @return The last component of this path.
280      */
281     public final QNameWithPredicate getLastComponent() {
282         return qname;
283     }
284
285     /**
286      * Describes whether schema path is|isn't absolute.
287      *
288      * @return boolean value which is <code>true</code> if schema path is
289      *         absolute.
290      */
291     public abstract boolean isAbsolute();
292
293     @Override
294     public final int hashCode() {
295         return hash;
296     }
297
298     @Override
299     public boolean equals(final Object obj) {
300         if (this == obj) {
301             return true;
302         }
303         if (obj == null) {
304             return false;
305         }
306         if (getClass() != obj.getClass()) {
307             return false;
308         }
309         final LeafRefPath other = (LeafRefPath) obj;
310         return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
311     }
312
313     @Override
314     public String toString() {
315         StringBuilder sb = new StringBuilder();
316
317         sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
318
319         for (QNameWithPredicate qname : getPathFromRoot()) {
320             sb.append('/').append(qname);
321         }
322
323         return sb.toString();
324     }
325
326     // @Override
327     // public final String toString() {
328     // return addToStringAttributes(Objects.toStringHelper(this)).toString();
329     // }
330     //
331     // protected ToStringHelper addToStringAttributes(final ToStringHelper
332     // toStringHelper) {
333     // return toStringHelper.add("path", getPathFromRoot());
334     // }
335
336 }