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.tree.leafref;
10 import static com.google.common.base.Preconditions.checkArgument;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.Iterables;
14 import java.lang.invoke.MethodHandles;
15 import java.lang.invoke.VarHandle;
16 import java.util.Arrays;
17 import java.util.Iterator;
18 import java.util.NoSuchElementException;
19 import java.util.Objects;
20 import org.opendaylight.yangtools.concepts.Immutable;
22 public abstract class LeafRefPath implements Immutable {
24 * An absolute LeafRefPath.
26 private static final class AbsoluteLeafRefPath extends LeafRefPath {
27 private AbsoluteLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
32 public boolean isAbsolute() {
37 protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
38 return new AbsoluteLeafRefPath(newParent, newQname);
43 * A relative LeafRefPath.
45 private static final class RelativeLeafRefPath extends LeafRefPath {
46 private RelativeLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
51 public boolean isAbsolute() {
56 protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
57 return new RelativeLeafRefPath(newParent, newQname);
61 private static final VarHandle LEGACYPATH;
65 LEGACYPATH = MethodHandles.lookup().findVarHandle(LeafRefPath.class, "legacyPath", ImmutableList.class);
66 } catch (NoSuchFieldException | IllegalAccessException e) {
67 throw new ExceptionInInitializerError(e);
72 * Shared instance of the conceptual root schema node.
74 public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
77 * Shared instance of the "same" relative schema node.
79 public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
84 private final LeafRefPath parent;
89 private final QNameWithPredicate qname;
92 * Cached hash code. We can use this since we are immutable.
94 private final int hash;
97 * Cached legacy path, filled-in when {@link #getPathFromRoot()} is invoked.
99 @SuppressWarnings("unused")
100 private volatile ImmutableList<QNameWithPredicate> legacyPath;
102 protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
103 this.parent = parent;
106 int hc = Objects.hashCode(parent);
108 hc = hc * 31 + qname.hashCode();
115 * Constructs new instance of this class with the concrete path.
117 * @param path list of QNameWithPredicate instances which specifies exact path to the module node
118 * @param absolute boolean value which specifies if the path is absolute or relative
119 * @return A LeafRefPath instance.
121 public static LeafRefPath create(final Iterable<QNameWithPredicate> path, final boolean absolute) {
122 final LeafRefPath parent = absolute ? ROOT : SAME;
123 return parent.createChild(path);
127 * Constructs new instance of this class with the concrete path.
129 * @param absolute boolean value which specifies if the path is absolute or relative
130 * @param path one or more QNameWithPredicate instances which specifies exact path to the module node
131 * @return A LeafRefPath instance.
133 public static LeafRefPath create(final boolean absolute, final QNameWithPredicate... path) {
134 return create(Arrays.asList(path), absolute);
138 * Create a new instance.
140 * @param newParent Parent LeafRefPath
141 * @param newQname next path element
142 * @return A new LeafRefPath instance
144 protected abstract LeafRefPath createInstance(LeafRefPath newParent, QNameWithPredicate newQname);
147 * Create a child path based on concatenation of this path and a relative path.
149 * @param relative Relative path
150 * @return A new child path
152 public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
153 if (Iterables.isEmpty(relative)) {
157 LeafRefPath newParent = this;
158 for (QNameWithPredicate relativeQname : relative) {
159 newParent = newParent.createInstance(newParent, relativeQname);
166 * Create a child path based on concatenation of this path and a relative path.
168 * @param relative Relative LeafRefPath
169 * @return A new child path
171 public LeafRefPath createChild(final LeafRefPath relative) {
172 checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
174 LeafRefPath newParent = this;
175 for (QNameWithPredicate relativeQname : relative.getPathFromRoot()) {
176 newParent = newParent.createInstance(newParent, relativeQname);
183 * Create a child path based on concatenation of this path and additional path elements.
185 * @param elements Relative LeafRefPath elements
186 * @return A new child path
188 public LeafRefPath createChild(final QNameWithPredicate... elements) {
189 return createChild(Arrays.asList(elements));
193 * Returns the list of nodes which need to be traversed to get from the starting point (root for absolute
194 * LeafRefPaths) to the node represented by this object.
196 * @return list of {@code qname} instances which represents path from the root to the schema node.
198 public Iterable<QNameWithPredicate> getPathFromRoot() {
199 final var local = (ImmutableList<QNameWithPredicate>) LEGACYPATH.getAcquire(this);
200 return local != null ? local : loadLegacyPath();
203 private ImmutableList<QNameWithPredicate> loadLegacyPath() {
204 final var ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
205 final var witness = (ImmutableList<QNameWithPredicate>) LEGACYPATH.compareAndExchangeRelease(this, null, ret);
206 return witness != null ? witness : ret;
210 * Returns the list of nodes which need to be traversed to get from this node to the starting point (root
211 * for absolute LeafRefPaths).
213 * @return list of {@code qname} instances which represents path from the schema node towards the root.
215 public Iterable<QNameWithPredicate> getPathTowardsRoot() {
216 return () -> new Iterator<>() {
217 private LeafRefPath current = LeafRefPath.this;
220 public boolean hasNext() {
221 return current.parent != null;
225 public QNameWithPredicate next() {
226 if (current.parent == null) {
227 throw new NoSuchElementException("No more elements available");
230 final QNameWithPredicate ret = current.qname;
231 current = current.parent;
238 * Returns the immediate parent LeafRefPath.
240 * @return Parent path, null if this LeafRefPath is already toplevel.
242 public LeafRefPath getParent() {
247 * Get the last component of this path.
249 * @return The last component of this path.
251 public final QNameWithPredicate getLastComponent() {
256 * Describes whether schema path is|isn't absolute.
258 * @return boolean value which is {@code true} if schema path is absolute.
260 public abstract boolean isAbsolute();
263 public final int hashCode() {
268 public boolean equals(final Object obj) {
272 if (obj == null || getClass() != obj.getClass()) {
275 final LeafRefPath other = (LeafRefPath) obj;
276 return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
280 public String toString() {
281 StringBuilder sb = new StringBuilder();
283 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
285 for (QNameWithPredicate qnameWithPredicate : getPathFromRoot()) {
286 sb.append('/').append(qnameWithPredicate);
289 return sb.toString();