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 = 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);
109 protected LeafRefPath(final LeafRefPath parent, final QNameWithPredicate qname) {
110 this.parent = parent;
113 int h = Objects.hashCode(parent);
115 h = h * 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(),
199 "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 final QNameWithPredicate ret = current.qname;
253 current = current.parent;
256 throw new NoSuchElementException(
257 "No more elements available");
262 public void remove() {
263 throw new UnsupportedOperationException(
264 "Component removal not supported");
270 * Returns the immediate parent LeafRefPath.
272 * @return Parent path, null if this LeafRefPath is already toplevel.
274 public LeafRefPath getParent() {
279 * Get the last component of this path.
281 * @return The last component of this path.
283 public final QNameWithPredicate getLastComponent() {
288 * Describes whether schema path is|isn't absolute.
290 * @return boolean value which is <code>true</code> if schema path is
293 public abstract boolean isAbsolute();
296 public final int hashCode() {
301 public boolean equals(final Object obj) {
308 if (getClass() != obj.getClass()) {
311 final LeafRefPath other = (LeafRefPath) obj;
312 return Objects.equals(qname, other.qname) && Objects.equals(parent, other.parent);
316 public String toString() {
317 StringBuilder sb = new StringBuilder();
319 Iterable<QNameWithPredicate> pathFromRoot = this.getPathFromRoot();
321 sb.append(isAbsolute() ? "Absolute path:" : "Relative path:");
323 for (QNameWithPredicate qName : pathFromRoot) {
324 sb.append('/').append(qName);
327 return sb.toString();
332 // public final String toString() {
333 // return addToStringAttributes(Objects.toStringHelper(this)).toString();
336 // protected ToStringHelper addToStringAttributes(final ToStringHelper
338 // return toStringHelper.add("path", getPathFromRoot());