Merge "BUG-994: introduce SchemaPath.getParent()"
[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 com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.Iterables;
13
14 import java.util.Arrays;
15 import java.util.Collections;
16 import java.util.List;
17
18 import org.opendaylight.yangtools.concepts.Immutable;
19 import org.opendaylight.yangtools.util.HashCodeBuilder;
20 import org.opendaylight.yangtools.yang.common.QName;
21
22 /**
23  *
24  * Represents unique path to the every node inside the module.
25  *
26  */
27 public class SchemaPath implements Immutable {
28     /**
29      * Shared instance of the conceptual root schema node.
30      */
31     public static final SchemaPath ROOT = new SchemaPath(Collections.<QName>emptyList(), true, Boolean.TRUE.hashCode());
32
33     /**
34      * Shared instance of the "same" relative schema node.
35      */
36     public static final SchemaPath SAME = new SchemaPath(Collections.<QName>emptyList(), false, Boolean.FALSE.hashCode());
37
38     /**
39      * List of QName instances which represents complete path to the node.
40      */
41     private final Iterable<QName> path;
42
43     /**
44      * Boolean value which represents type of schema path (relative or
45      * absolute).
46      */
47     private final boolean absolute;
48
49     /**
50      * Cached hash code. We can use this since we are immutable.
51      */
52     private final int hash;
53
54     /**
55      * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()}
56      * is invoked.
57      */
58     private ImmutableList<QName> legacyPath;
59
60     /**
61      * Constructs new instance of this class with the concrete path.
62      *
63      * @param path
64      *            list of QName instances which specifies exact path to the
65      *            module node
66      * @param absolute
67      *            boolean value which specifies if the path is absolute or
68      *            relative
69      *
70      * @deprecated Use {@link #create(Iterable, boolean)} instead.
71      */
72     @Deprecated
73     public SchemaPath(final List<QName> path, final boolean absolute) {
74         this(ImmutableList.copyOf(path), absolute, Boolean.valueOf(absolute).hashCode());
75     }
76
77     private ImmutableList<QName> getLegacyPath() {
78         if (legacyPath == null) {
79             legacyPath = ImmutableList.copyOf(path);
80         }
81
82         return legacyPath;
83     }
84
85     /**
86      * Returns the complete path to schema node.
87      *
88      * @return list of <code>QName</code> instances which represents complete
89      *         path to schema node
90      *
91      * @deprecated Use {@link #getPathFromRoot()} instead.
92      */
93     @Deprecated
94     public List<QName> getPath() {
95         return getLegacyPath();
96     }
97
98     private SchemaPath(final Iterable<QName> path, final boolean absolute, final int hash) {
99         this.path = Preconditions.checkNotNull(path);
100         this.absolute = absolute;
101         this.hash = hash;
102     }
103
104     /**
105      * Constructs new instance of this class with the concrete path.
106      *
107      * @param path
108      *            list of QName instances which specifies exact path to the
109      *            module node
110      * @param absolute
111      *            boolean value which specifies if the path is absolute or
112      *            relative
113      *
114      * @return A SchemaPath instance.
115      */
116     public static SchemaPath create(final Iterable<QName> path, final boolean absolute) {
117         final SchemaPath parent = absolute ? ROOT : SAME;
118         return parent.createChild(path);
119     }
120
121     /**
122      * Constructs new instance of this class with the concrete path.
123      *
124      * @param absolute
125      *            boolean value which specifies if the path is absolute or
126      *            relative
127      * @param path
128      *            one or more QName instances which specifies exact path to the
129      *            module node
130      *
131      * @return A SchemaPath instance.
132      */
133     public static SchemaPath create(final boolean absolute, final QName... path) {
134         return create(Arrays.asList(path), absolute);
135     }
136
137     private SchemaPath trustedCreateChild(final Iterable<QName> relative) {
138         if (Iterables.isEmpty(relative)) {
139             return this;
140         }
141
142         final HashCodeBuilder<QName> b = new HashCodeBuilder<>(hash);
143         for (QName p : relative) {
144             b.addArgument(p);
145         }
146
147         return new SchemaPath(Iterables.concat(path, relative), absolute, b.toInstance());
148     }
149
150     /**
151      * Create a child path based on concatenation of this path and a relative path.
152      *
153      * @param relative Relative path
154      * @return A new child path
155      */
156     public SchemaPath createChild(final Iterable<QName> relative) {
157         if (Iterables.isEmpty(relative)) {
158             return this;
159         }
160
161         return trustedCreateChild(ImmutableList.copyOf(relative));
162     }
163
164     /**
165      * Create a child path based on concatenation of this path and a relative path.
166      *
167      * @param relative Relative SchemaPath
168      * @return A new child path
169      */
170     public SchemaPath createChild(final SchemaPath relative) {
171         Preconditions.checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
172         return trustedCreateChild(relative.path);
173     }
174
175     /**
176      * Create a child path based on concatenation of this path and additional
177      * path elements.
178      *
179      * @param elements Relative SchemaPath elements
180      * @return A new child path
181      */
182     public SchemaPath createChild(final QName... elements) {
183         return createChild(Arrays.asList(elements));
184     }
185
186     /**
187      * Returns the list of nodes which need to be traversed to get from the
188      * starting point (root for absolute SchemaPaths) to the node represented
189      * by this object.
190      *
191      * @return list of <code>qname</code> instances which represents
192      *         path from the root to the schema node.
193      */
194     public Iterable<QName> getPathFromRoot() {
195         return path;
196     }
197
198     /**
199      * Returns the list of nodes which need to be traversed to get from this
200      * node to the starting point (root for absolute SchemaPaths).
201      *
202      * @return list of <code>qname</code> instances which represents
203      *         path from the schema node towards the root.
204      */
205     public Iterable<QName> getPathTowardsRoot() {
206         return getLegacyPath().reverse();
207     }
208
209     /**
210      * Returns the immediate parent SchemaPath.
211      *
212      * @return Parent path, null if this SchemaPath is already toplevel.
213      */
214     public SchemaPath getParent() {
215         final int size = Iterables.size(path);
216         if (size != 0) {
217             final SchemaPath parent = isAbsolute() ? ROOT : SAME;
218             return parent.trustedCreateChild(Iterables.limit(path, size - 1));
219         } else {
220             return null;
221         }
222     }
223
224     /**
225      * Describes whether schema path is|isn't absolute.
226      *
227      * @return boolean value which is <code>true</code> if schema path is
228      *         absolute.
229      */
230     public boolean isAbsolute() {
231         return absolute;
232     }
233
234     @Override
235     public int hashCode() {
236         return hash;
237     }
238
239     @Override
240     public boolean equals(final Object obj) {
241         if (this == obj) {
242             return true;
243         }
244         if (obj == null) {
245             return false;
246         }
247         if (getClass() != obj.getClass()) {
248             return false;
249         }
250         SchemaPath other = (SchemaPath) obj;
251         if (absolute != other.absolute) {
252             return false;
253         }
254
255         return Iterables.elementsEqual(path, other.path);
256     }
257
258     @Override
259     public String toString() {
260         StringBuilder builder = new StringBuilder();
261         builder.append("SchemaPath [path=");
262         builder.append(path);
263         builder.append(", absolute=");
264         builder.append(absolute);
265         builder.append("]");
266         return builder.toString();
267     }
268 }