Fix checkstyle issues reported by odlparent-3.0.0's checkstyle
[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         public AbsoluteSchemaPath createChild(final QName element) {
48             return new AbsoluteSchemaPath(this, requireNonNull(element));
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         public RelativeSchemaPath createChild(final QName element) {
67             return new RelativeSchemaPath(this, requireNonNull(element));
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 item : getPathTowardsRoot()) {
123                 tmp.add(item);
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 child path based on concatenation of this path and a relative path.
180      *
181      * @param relative Relative path
182      * @return A new child path
183      */
184     public SchemaPath createChild(final Iterable<QName> relative) {
185         if (Iterables.isEmpty(relative)) {
186             return this;
187         }
188
189         SchemaPath parentPath = this;
190         for (QName item : relative) {
191             parentPath = parentPath.createChild(item);
192         }
193
194         return parentPath;
195     }
196
197     /**
198      * Create a child path based on concatenation of this path and a relative path.
199      *
200      * @param relative Relative SchemaPath
201      * @return A new child path
202      */
203     public SchemaPath createChild(final SchemaPath relative) {
204         checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
205         return createChild(relative.getPathFromRoot());
206     }
207
208     /**
209      * Create a child path based on concatenation of this path and an additional path element.
210      *
211      * @param element Relative SchemaPath elements
212      * @return A new child path
213      */
214     public abstract SchemaPath createChild(QName element);
215
216     /**
217      * Create a child path based on concatenation of this path and additional
218      * path elements.
219      *
220      * @param elements Relative SchemaPath elements
221      * @return A new child path
222      */
223     public SchemaPath createChild(final QName... elements) {
224         return createChild(Arrays.asList(elements));
225     }
226
227     /**
228      * Returns the list of nodes which need to be traversed to get from the
229      * starting point (root for absolute SchemaPaths) to the node represented
230      * by this object.
231      *
232      * @return list of <code>qname</code> instances which represents
233      *         path from the root to the schema node.
234      */
235     public Iterable<QName> getPathFromRoot() {
236         return getLegacyPath();
237     }
238
239     /**
240      * Returns the list of nodes which need to be traversed to get from this
241      * node to the starting point (root for absolute SchemaPaths).
242      *
243      * @return list of <code>qname</code> instances which represents
244      *         path from the schema node towards the root.
245      */
246     public Iterable<QName> getPathTowardsRoot() {
247         return () -> new UnmodifiableIterator<QName>() {
248             private SchemaPath current = SchemaPath.this;
249
250             @Override
251             public boolean hasNext() {
252                 return current.parent != null;
253             }
254
255             @Override
256             public QName next() {
257                 if (current.parent != null) {
258                     final QName ret = current.qname;
259                     current = current.parent;
260                     return ret;
261                 } else {
262                     throw new NoSuchElementException("No more elements available");
263                 }
264             }
265         };
266     }
267
268     /**
269      * Returns the immediate parent SchemaPath.
270      *
271      * @return Parent path, null if this SchemaPath is already toplevel.
272      */
273     public SchemaPath getParent() {
274         return parent;
275     }
276
277     /**
278      * Get the last component of this path.
279      *
280      * @return The last component of this path.
281      */
282     public final QName getLastComponent() {
283         return qname;
284     }
285
286     /**
287      * Describes whether schema path is|isn't absolute.
288      *
289      * @return boolean value which is <code>true</code> if schema path is
290      *         absolute.
291      */
292     public abstract boolean isAbsolute();
293
294     @Override
295     public final int hashCode() {
296         return hash;
297     }
298
299     @Override
300     public boolean equals(final Object obj) {
301         if (this == obj) {
302             return true;
303         }
304         if (obj == null) {
305             return false;
306         }
307         if (getClass() != obj.getClass()) {
308             return false;
309         }
310         final SchemaPath other = (SchemaPath) obj;
311         return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
312     }
313
314     @Override
315     public final String toString() {
316         return addToStringAttributes(MoreObjects.toStringHelper(this)).toString();
317     }
318
319     protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
320         return toStringHelper.add("path", getPathFromRoot());
321     }
322 }