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