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 static com.google.common.base.Preconditions.checkArgument;
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.NoSuchElementException;
17 import java.util.Objects;
18 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
19 import org.opendaylight.yangtools.concepts.Immutable;
21 public abstract class LeafRefPath implements Immutable {
23 * An absolute LeafRefPath.
25 private static final class AbsoluteLeafRefPath extends LeafRefPath {
26 private AbsoluteLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
31 public boolean isAbsolute() {
36 protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
37 return new AbsoluteLeafRefPath(newParent, newQname);
42 * A relative LeafRefPath.
44 private static final class RelativeLeafRefPath extends LeafRefPath {
45 private RelativeLeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
50 public boolean isAbsolute() {
55 protected LeafRefPath createInstance(final LeafRefPath newParent, final QNameWithPredicate newQname) {
56 return new RelativeLeafRefPath(newParent, newQname);
60 @SuppressWarnings("rawtypes")
61 private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER =
62 AtomicReferenceFieldUpdater.newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
65 * Shared instance of the conceptual root schema node.
67 public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
70 * Shared instance of the "same" relative schema node.
72 public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
77 private final LeafRefPath parent;
82 private final QNameWithPredicate qname;
85 * Cached hash code. We can use this since we are immutable.
87 private final int hash;
90 * Cached legacy path, filled-in when {@link #getPathFromRoot()} or {@link #getPathTowardsRoot()} is invoked.
92 private volatile ImmutableList<QNameWithPredicate> legacyPath;
94 private ImmutableList<QNameWithPredicate> getLegacyPath() {
95 ImmutableList<QNameWithPredicate> ret = legacyPath;
97 ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
98 LEGACYPATH_UPDATER.lazySet(this, ret);
104 protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
105 this.parent = parent;
108 int hc = Objects.hashCode(parent);
110 hc = hc * 31 + qname.hashCode();
117 * Constructs new instance of this class with the concrete path.
119 * @param path list of QNameWithPredicate instances which specifies exact path to the module node
120 * @param absolute boolean value which specifies if the path is absolute or relative
121 * @return A LeafRefPath instance.
123 public static LeafRefPath create(final Iterable<QNameWithPredicate> path, final boolean absolute) {
124 final LeafRefPath parent = absolute ? ROOT : SAME;
125 return parent.createChild(path);
129 * Constructs new instance of this class with the concrete path.
131 * @param absolute boolean value which specifies if the path is absolute or relative
132 * @param path one or more QNameWithPredicate instances which specifies exact path to the module node
133 * @return A LeafRefPath instance.
135 public static LeafRefPath create(final boolean absolute, final QNameWithPredicate... path) {
136 return create(Arrays.asList(path), absolute);
140 * Create a new instance.
142 * @param newParent Parent LeafRefPath
143 * @param newQname next path element
144 * @return A new LeafRefPath instance
146 protected abstract LeafRefPath createInstance(LeafRefPath newParent, QNameWithPredicate newQname);
149 * Create a child path based on concatenation of this path and a relative path.
151 * @param relative Relative path
152 * @return A new child path
154 public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
155 if (Iterables.isEmpty(relative)) {
159 LeafRefPath newParent = this;
160 for (QNameWithPredicate relativeQname : relative) {
161 newParent = newParent.createInstance(newParent, relativeQname);
168 * Create a child path based on concatenation of this path and a relative path.
170 * @param relative Relative LeafRefPath
171 * @return A new child path
173 public LeafRefPath createChild(final LeafRefPath relative) {
174 checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
176 LeafRefPath newParent = this;
177 for (QNameWithPredicate relativeQname : relative.getPathFromRoot()) {
178 newParent = newParent.createInstance(newParent, relativeQname);
185 * Create a child path based on concatenation of this path and additional path elements.
187 * @param elements Relative LeafRefPath elements
188 * @return A new child path
190 public LeafRefPath createChild(final QNameWithPredicate... elements) {
191 return createChild(Arrays.asList(elements));
195 * Returns the list of nodes which need to be traversed to get from the starting point (root for absolute
196 * LeafRefPaths) to the node represented by this object.
198 * @return list of {@code qname} instances which represents path from the root to the schema node.
200 public Iterable<QNameWithPredicate> getPathFromRoot() {
201 return getLegacyPath();
205 * Returns the list of nodes which need to be traversed to get from this node to the starting point (root
206 * for absolute LeafRefPaths).
208 * @return list of {@code qname} instances which represents path from the schema node towards the root.
210 public Iterable<QNameWithPredicate> getPathTowardsRoot() {
211 return () -> new Iterator<QNameWithPredicate>() {
212 private LeafRefPath current = LeafRefPath.this;
215 public boolean hasNext() {
216 return current.parent != null;
220 public QNameWithPredicate next() {
221 if (current.parent == null) {
222 throw new NoSuchElementException("No more elements available");
225 final QNameWithPredicate ret = current.qname;
226 current = current.parent;
233 * Returns the immediate parent LeafRefPath.
235 * @return Parent path, null if this LeafRefPath is already toplevel.
237 public LeafRefPath getParent() {
242 * Get the last component of this path.
244 * @return The last component of this path.
246 public final QNameWithPredicate getLastComponent() {
251 * Describes whether schema path is|isn't absolute.
253 * @return boolean value which is {@code true} if schema path is absolute.
255 public abstract boolean isAbsolute();
258 public final int hashCode() {
263 public boolean equals(final Object obj) {
267 if (obj == null || getClass() != obj.getClass()) {
270 final LeafRefPath other = (LeafRefPath) obj;
271 return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
275 public String toString() {
276 StringBuilder sb = new StringBuilder();
278 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
280 for (QNameWithPredicate qnameWithPredicate : getPathFromRoot()) {
281 sb.append('/').append(qnameWithPredicate);
284 return sb.toString();