2 * Copyright (c) 2015 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.impl.leafref;
10 import org.opendaylight.yangtools.concepts.Immutable;
11 import com.google.common.base.Preconditions;
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.List;
17 import java.util.NoSuchElementException;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
20 public abstract class LeafRefPath implements Immutable {
23 * An absolute LeafRefPath.
25 private static final class AbsoluteLeafRefPath extends LeafRefPath {
26 private AbsoluteLeafRefPath(final LeafRefPath parent,
27 final QNameWithPredicate qname) {
32 public boolean isAbsolute() {
37 protected LeafRefPath createInstance(final LeafRefPath parent,
38 final QNameWithPredicate qname) {
39 return new AbsoluteLeafRefPath(parent, qname);
44 * A relative LeafRefPath.
46 private static final class RelativeLeafRefPath extends LeafRefPath {
47 private RelativeLeafRefPath(final LeafRefPath parent,
48 final QNameWithPredicate qname) {
53 public boolean isAbsolute() {
58 protected LeafRefPath createInstance(final LeafRefPath parent,
59 final QNameWithPredicate qname) {
60 return new RelativeLeafRefPath(parent, qname);
64 @SuppressWarnings("rawtypes")
65 private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER = AtomicReferenceFieldUpdater
66 .newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
69 * Shared instance of the conceptual root schema node.
71 public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
74 * Shared instance of the "same" relative schema node.
76 public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
81 private final LeafRefPath parent;
86 private final QNameWithPredicate qname;
89 * Cached hash code. We can use this since we are immutable.
91 private final int hash;
94 * Cached legacy path, filled-in when {@link #getPath()} or
95 * {@link #getPathTowardsRoot()} is invoked.
97 private volatile ImmutableList<QNameWithPredicate> legacyPath;
99 private ImmutableList<QNameWithPredicate> getLegacyPath() {
100 ImmutableList<QNameWithPredicate> ret = legacyPath;
102 ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
103 LEGACYPATH_UPDATER.lazySet(this, ret);
110 * Returns the complete path to schema node.
112 * @return list of <code>QNameWithPredicate</code> instances which
113 * represents complete path to schema node
115 * @deprecated Use {@link #getPathFromRoot()} instead.
118 public List<QNameWithPredicate> getPath() {
119 return getLegacyPath();
122 protected LeafRefPath(final LeafRefPath parent,
123 final QNameWithPredicate qname) {
124 this.parent = parent;
127 int h = parent == null ? 0 : parent.hashCode();
129 h = h * 31 + qname.hashCode();
136 * Constructs new instance of this class with the concrete path.
139 * list of QNameWithPredicate instances which specifies exact
140 * path to the module node
142 * boolean value which specifies if the path is absolute or
145 * @return A LeafRefPath instance.
147 public static LeafRefPath create(final Iterable<QNameWithPredicate> path,
148 final boolean absolute) {
149 final LeafRefPath parent = absolute ? ROOT : SAME;
150 return parent.createChild(path);
154 * Constructs new instance of this class with the concrete path.
157 * boolean value which specifies if the path is absolute or
160 * one or more QNameWithPredicate instances which specifies exact
161 * path to the module node
163 * @return A LeafRefPath instance.
165 public static LeafRefPath create(final boolean absolute,
166 final QNameWithPredicate... path) {
167 return create(Arrays.asList(path), absolute);
171 * Create a new instance.
177 * @return A new LeafRefPath instance
179 protected abstract LeafRefPath createInstance(LeafRefPath parent,
180 QNameWithPredicate qname);
183 * Create a child path based on concatenation of this path and a relative
188 * @return A new child path
190 public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
191 if (Iterables.isEmpty(relative)) {
195 LeafRefPath parent = this;
196 for (QNameWithPredicate qname : relative) {
197 parent = parent.createInstance(parent, qname);
204 * Create a child path based on concatenation of this path and a relative
208 * Relative LeafRefPath
209 * @return A new child path
211 public LeafRefPath createChild(final LeafRefPath relative) {
212 Preconditions.checkArgument(!relative.isAbsolute(),
213 "Child creation requires relative path");
215 LeafRefPath parent = this;
216 for (QNameWithPredicate qname : relative.getPathFromRoot()) {
217 parent = parent.createInstance(parent, qname);
224 * Create a child path based on concatenation of this path and additional
228 * Relative LeafRefPath elements
229 * @return A new child path
231 public LeafRefPath createChild(final QNameWithPredicate... elements) {
232 return createChild(Arrays.asList(elements));
236 * Returns the list of nodes which need to be traversed to get from the
237 * starting point (root for absolute LeafRefPaths) to the node represented
240 * @return list of <code>qname</code> instances which represents path from
241 * the root to the schema node.
243 public Iterable<QNameWithPredicate> getPathFromRoot() {
244 return getLegacyPath();
248 * Returns the list of nodes which need to be traversed to get from this
249 * node to the starting point (root for absolute LeafRefPaths).
251 * @return list of <code>qname</code> instances which represents path from
252 * the schema node towards the root.
254 public Iterable<QNameWithPredicate> getPathTowardsRoot() {
255 return new Iterable<QNameWithPredicate>() {
257 public Iterator<QNameWithPredicate> iterator() {
258 return new Iterator<QNameWithPredicate>() {
259 private LeafRefPath current = LeafRefPath.this;
262 public boolean hasNext() {
263 return current.parent != null;
267 public QNameWithPredicate next() {
268 if (current.parent != null) {
269 final QNameWithPredicate ret = current.qname;
270 current = current.parent;
273 throw new NoSuchElementException(
274 "No more elements available");
279 public void remove() {
280 throw new UnsupportedOperationException(
281 "Component removal not supported");
289 * Returns the immediate parent LeafRefPath.
291 * @return Parent path, null if this LeafRefPath is already toplevel.
293 public LeafRefPath getParent() {
298 * Get the last component of this path.
300 * @return The last component of this path.
302 public final QNameWithPredicate getLastComponent() {
307 * Describes whether schema path is|isn't absolute.
309 * @return boolean value which is <code>true</code> if schema path is
312 public abstract boolean isAbsolute();
315 public final int hashCode() {
320 public boolean equals(final Object obj) {
327 if (getClass() != obj.getClass()) {
330 final LeafRefPath other = (LeafRefPath) obj;
333 if (!qname.equals(other.qname)) {
337 if (other.qname != null) {
342 if (parent == null) {
343 return other.parent == null;
345 return parent.equals(other.parent);
349 public String toString() {
350 StringBuilder sb = new StringBuilder();
352 Iterable<QNameWithPredicate> pathFromRoot = this.getPathFromRoot();
354 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
356 for (QNameWithPredicate qName : pathFromRoot) {
357 sb.append("/" + qName);
360 return sb.toString();
365 // public final String toString() {
366 // return addToStringAttributes(Objects.toStringHelper(this)).toString();
369 // protected ToStringHelper addToStringAttributes(final ToStringHelper
371 // return toStringHelper.add("path", getPathFromRoot());