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 com.google.common.base.Preconditions;
11 import com.google.common.collect.ImmutableList;
12 import com.google.common.collect.Iterables;
13 import java.util.Arrays;
14 import java.util.Iterator;
15 import java.util.NoSuchElementException;
16 import java.util.Objects;
17 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
18 import org.opendaylight.yangtools.concepts.Immutable;
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 =
66 AtomicReferenceFieldUpdater.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);
109 protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
110 this.parent = parent;
113 int hc = Objects.hashCode(parent);
115 hc = hc * 31 + qname.hashCode();
122 * Constructs new instance of this class with the concrete path.
125 * list of QNameWithPredicate instances which specifies exact
126 * path to the module node
128 * boolean value which specifies if the path is absolute or
131 * @return A LeafRefPath instance.
133 public static LeafRefPath create(final Iterable<QNameWithPredicate> path,
134 final boolean absolute) {
135 final LeafRefPath parent = absolute ? ROOT : SAME;
136 return parent.createChild(path);
140 * Constructs new instance of this class with the concrete path.
143 * boolean value which specifies if the path is absolute or
146 * one or more QNameWithPredicate instances which specifies exact
147 * path to the module node
149 * @return A LeafRefPath instance.
151 public static LeafRefPath create(final boolean absolute,
152 final QNameWithPredicate... path) {
153 return create(Arrays.asList(path), absolute);
157 * Create a new instance.
163 * @return A new LeafRefPath instance
165 protected abstract LeafRefPath createInstance(LeafRefPath parent,
166 QNameWithPredicate qname);
169 * Create a child path based on concatenation of this path and a relative
174 * @return A new child path
176 public LeafRefPath createChild(final Iterable<QNameWithPredicate> relative) {
177 if (Iterables.isEmpty(relative)) {
181 LeafRefPath parent = this;
182 for (QNameWithPredicate qname : relative) {
183 parent = parent.createInstance(parent, qname);
190 * Create a child path based on concatenation of this path and a relative
194 * Relative LeafRefPath
195 * @return A new child path
197 public LeafRefPath createChild(final LeafRefPath relative) {
198 Preconditions.checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
200 LeafRefPath parent = this;
201 for (QNameWithPredicate qname : relative.getPathFromRoot()) {
202 parent = parent.createInstance(parent, qname);
209 * Create a child path based on concatenation of this path and additional
213 * Relative LeafRefPath elements
214 * @return A new child path
216 public LeafRefPath createChild(final QNameWithPredicate... elements) {
217 return createChild(Arrays.asList(elements));
221 * Returns the list of nodes which need to be traversed to get from the
222 * starting point (root for absolute LeafRefPaths) to the node represented
225 * @return list of <code>qname</code> instances which represents path from
226 * the root to the schema node.
228 public Iterable<QNameWithPredicate> getPathFromRoot() {
229 return getLegacyPath();
233 * Returns the list of nodes which need to be traversed to get from this
234 * node to the starting point (root for absolute LeafRefPaths).
236 * @return list of <code>qname</code> instances which represents path from
237 * the schema node towards the root.
239 public Iterable<QNameWithPredicate> getPathTowardsRoot() {
240 return () -> new Iterator<QNameWithPredicate>() {
241 private LeafRefPath current = LeafRefPath.this;
244 public boolean hasNext() {
245 return current.parent != null;
249 public QNameWithPredicate next() {
250 if (current.parent == null) {
251 throw new NoSuchElementException("No more elements available");
254 final QNameWithPredicate ret = current.qname;
255 current = current.parent;
260 public void remove() {
261 throw new UnsupportedOperationException("Component removal not supported");
267 * Returns the immediate parent LeafRefPath.
269 * @return Parent path, null if this LeafRefPath is already toplevel.
271 public LeafRefPath getParent() {
276 * Get the last component of this path.
278 * @return The last component of this path.
280 public final QNameWithPredicate getLastComponent() {
285 * Describes whether schema path is|isn't absolute.
287 * @return boolean value which is <code>true</code> if schema path is
290 public abstract boolean isAbsolute();
293 public final int hashCode() {
298 public boolean equals(final Object obj) {
305 if (getClass() != obj.getClass()) {
308 final LeafRefPath other = (LeafRefPath) obj;
309 return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
313 public String toString() {
314 StringBuilder sb = new StringBuilder();
316 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
318 for (QNameWithPredicate qname : getPathFromRoot()) {
319 sb.append('/').append(qname);
322 return sb.toString();
326 // public final String toString() {
327 // return addToStringAttributes(Objects.toStringHelper(this)).toString();
330 // protected ToStringHelper addToStringAttributes(final ToStringHelper
332 // return toStringHelper.add("path", getPathFromRoot());