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