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 {
24 * An absolute LeafRefPath.
26 private static final class AbsoluteLeafRefPath extends LeafRefPath {
27 private AbsoluteLeafRefPath(final LeafRefPath parent,
28 final QNameWithPredicate qname) {
33 public boolean isAbsolute() {
38 protected LeafRefPath createInstance(final LeafRefPath parent,
39 final QNameWithPredicate qname) {
40 return new AbsoluteLeafRefPath(parent, qname);
45 * A relative LeafRefPath.
47 private static final class RelativeLeafRefPath extends LeafRefPath {
48 private RelativeLeafRefPath(final LeafRefPath parent,
49 final QNameWithPredicate qname) {
54 public boolean isAbsolute() {
59 protected LeafRefPath createInstance(final LeafRefPath parent,
60 final QNameWithPredicate qname) {
61 return new RelativeLeafRefPath(parent, qname);
65 @SuppressWarnings("rawtypes")
66 private static final AtomicReferenceFieldUpdater<LeafRefPath, ImmutableList> LEGACYPATH_UPDATER =
67 AtomicReferenceFieldUpdater.newUpdater(LeafRefPath.class, ImmutableList.class, "legacyPath");
70 * Shared instance of the conceptual root schema node.
72 public static final LeafRefPath ROOT = new AbsoluteLeafRefPath(null, null);
75 * Shared instance of the "same" relative schema node.
77 public static final LeafRefPath SAME = new RelativeLeafRefPath(null, null);
82 private final LeafRefPath parent;
87 private final QNameWithPredicate qname;
90 * Cached hash code. We can use this since we are immutable.
92 private final int hash;
95 * Cached legacy path, filled-in when {@link #getPath()} or
96 * {@link #getPathTowardsRoot()} is invoked.
98 private volatile ImmutableList<QNameWithPredicate> legacyPath;
100 private ImmutableList<QNameWithPredicate> getLegacyPath() {
101 ImmutableList<QNameWithPredicate> ret = legacyPath;
103 ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
104 LEGACYPATH_UPDATER.lazySet(this, ret);
110 protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
111 this.parent = parent;
114 int hc = Objects.hashCode(parent);
116 hc = hc * 31 + qname.hashCode();
123 * Constructs new instance of this class with the concrete path.
126 * list of QNameWithPredicate instances which specifies exact
127 * path to the module node
129 * boolean value which specifies if the path is absolute or
132 * @return A LeafRefPath instance.
134 public static LeafRefPath create(final Iterable<QNameWithPredicate> path,
135 final boolean absolute) {
136 final LeafRefPath parent = absolute ? ROOT : SAME;
137 return parent.createChild(path);
141 * Constructs new instance of this class with the concrete path.
144 * boolean value which specifies if the path is absolute or
147 * one or more QNameWithPredicate instances which specifies exact
148 * path to the module node
150 * @return A LeafRefPath instance.
152 public static LeafRefPath create(final boolean absolute,
153 final QNameWithPredicate... path) {
154 return create(Arrays.asList(path), absolute);
158 * Create a new instance.
164 * @return A new LeafRefPath instance
166 protected abstract LeafRefPath createInstance(LeafRefPath parent,
167 QNameWithPredicate qname);
170 * Create a child path based on concatenation of this path and a relative
175 * @return A new child path
177 public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
178 if (Iterables.isEmpty(relative)) {
182 LeafRefPath parent = this;
183 for (QNameWithPredicate qname : relative) {
184 parent = parent.createInstance(parent, qname);
191 * Create a child path based on concatenation of this path and a relative
195 * Relative LeafRefPath
196 * @return A new child path
198 public LeafRefPath createChild(final LeafRefPath relative) {
199 checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
201 LeafRefPath parent = this;
202 for (QNameWithPredicate qname : relative.getPathFromRoot()) {
203 parent = parent.createInstance(parent, qname);
210 * Create a child path based on concatenation of this path and additional
214 * Relative LeafRefPath elements
215 * @return A new child path
217 public LeafRefPath createChild(final QNameWithPredicate... elements) {
218 return createChild(Arrays.asList(elements));
222 * Returns the list of nodes which need to be traversed to get from the
223 * starting point (root for absolute LeafRefPaths) to the node represented
226 * @return list of <code>qname</code> instances which represents path from
227 * the root to the schema node.
229 public Iterable<QNameWithPredicate> getPathFromRoot() {
230 return getLegacyPath();
234 * Returns the list of nodes which need to be traversed to get from this
235 * node to the starting point (root for absolute LeafRefPaths).
237 * @return list of <code>qname</code> instances which represents path from
238 * the schema node towards the root.
240 public Iterable<QNameWithPredicate> getPathTowardsRoot() {
241 return () -> new Iterator<QNameWithPredicate>() {
242 private LeafRefPath current = LeafRefPath.this;
245 public boolean hasNext() {
246 return current.parent != null;
250 public QNameWithPredicate next() {
251 if (current.parent == null) {
252 throw new NoSuchElementException("No more elements available");
255 final QNameWithPredicate ret = current.qname;
256 current = current.parent;
261 public void remove() {
262 throw new UnsupportedOperationException("Component removal not supported");
268 * Returns the immediate parent LeafRefPath.
270 * @return Parent path, null if this LeafRefPath is already toplevel.
272 public LeafRefPath getParent() {
277 * Get the last component of this path.
279 * @return The last component of this path.
281 public final QNameWithPredicate getLastComponent() {
286 * Describes whether schema path is|isn't absolute.
288 * @return boolean value which is <code>true</code> if schema path is
291 public abstract boolean isAbsolute();
294 public final int hashCode() {
299 public boolean equals(final Object obj) {
306 if (getClass() != obj.getClass()) {
309 final LeafRefPath other = (LeafRefPath) obj;
310 return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
314 public String toString() {
315 StringBuilder sb = new StringBuilder();
317 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
319 for (QNameWithPredicate qname : getPathFromRoot()) {
320 sb.append('/').append(qname);
323 return sb.toString();
327 // public final String toString() {
328 // return addToStringAttributes(Objects.toStringHelper(this)).toString();
331 // protected ToStringHelper addToStringAttributes(final ToStringHelper
333 // return toStringHelper.add("path", getPathFromRoot());