*/
package org.opendaylight.yangtools.yang.model.api.stmt;
-import com.google.common.base.Objects;
-import com.google.common.base.Objects.ToStringHelper;
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Iterables;
import java.util.Arrays;
-import java.util.Iterator;
+import java.util.Collection;
import java.util.List;
-import java.util.NoSuchElementException;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
/**
- * Represents unique path to the every schema node inside the schema node identifier
- * namespace.
- *
+ * Represents unique path to the every schema node inside the schema node identifier namespace. This concept is defined
+ * in <a href="https://tools.ietf.org/html/rfc7950#section-6.5">RFC7950</a>.
*/
public abstract class SchemaNodeIdentifier implements Immutable {
-
/**
* An absolute schema node identifier.
*/
public static final class Absolute extends SchemaNodeIdentifier {
- private Absolute(final SchemaNodeIdentifier parent, final QName qname) {
- super(parent, qname);
+ Absolute(final QName qname) {
+ super(qname);
}
- @Override
- public boolean isAbsolute() {
- return true;
+ Absolute(final Collection<QName> qnames) {
+ super(qnames);
}
- @Override
- protected SchemaNodeIdentifier createInstance(final SchemaNodeIdentifier parent, final QName qname) {
- return new Absolute(parent, qname);
+ public static Absolute of(final QName nodeIdentifier) {
+ return new Absolute(nodeIdentifier);
}
- }
- /**
- * A relative schema node identifier.
- */
- public static class Relative extends SchemaNodeIdentifier {
- private Relative(final SchemaNodeIdentifier parent, final QName qname) {
- super(parent, qname);
+ public static Absolute of(final QName... nodeIdentifiers) {
+ return new Absolute(Arrays.asList(nodeIdentifiers));
}
- @Override
- public boolean isAbsolute() {
- return false;
+ public static Absolute of(final Collection<QName> nodeIdentifiers) {
+ return new Absolute(ImmutableList.copyOf(nodeIdentifiers));
}
@Override
- protected SchemaNodeIdentifier createInstance(final SchemaNodeIdentifier parent, final QName qname) {
- return new Relative(parent, qname);
+ SchemaPath implicitSchemaPathParent() {
+ return SchemaPath.ROOT;
}
}
- @SuppressWarnings("rawtypes")
- private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, ImmutableList> LEGACYPATH_UPDATER =
- AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, ImmutableList.class, "legacyPath");
-
- /**
- * Shared instance of the conceptual root schema node.
- */
- public static final SchemaNodeIdentifier ROOT = new Absolute(null, null);
-
- /**
- * Shared instance of the "same" relative schema node.
- */
- public static final SchemaNodeIdentifier SAME = new Relative(null, null);
-
- /**
- * Parent path.
- */
- private final SchemaNodeIdentifier parent;
-
- /**
- * This component.
- */
- private final QName qname;
-
- /**
- * Cached hash code. We can use this since we are immutable.
- */
- private final int hash;
-
/**
- * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()}
- * is invoked.
+ * A descendant schema node identifier.
*/
- private volatile ImmutableList<QName> legacyPath;
-
- protected SchemaNodeIdentifier(final SchemaNodeIdentifier parent, final QName qname) {
- this.parent = parent;
- this.qname = qname;
-
- int h = parent == null ? 0 : parent.hashCode();
- if (qname != null) {
- h = h * 31 + qname.hashCode();
+ public static final class Descendant extends SchemaNodeIdentifier {
+ Descendant(final QName qname) {
+ super(qname);
}
- hash = h;
- }
-
- private ImmutableList<QName> getLegacyPath() {
- ImmutableList<QName> ret = legacyPath;
- if (ret == null) {
- ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
- LEGACYPATH_UPDATER.lazySet(this, ret);
+ Descendant(final Collection<QName> qnames) {
+ super(qnames);
}
- return ret;
- }
-
- /**
- * Returns the complete path to schema node.
- *
- * @return list of <code>QName</code> instances which represents complete
- * path to schema node
- *
- * @deprecated Use {@link #getPathFromRoot()} instead.
- */
- @Deprecated
- public List<QName> getPath() {
- return getLegacyPath();
- }
-
- /**
- * Constructs new instance of this class with the concrete path.
- *
- * @param path
- * list of QName instances which specifies exact path to the
- * module node
- * @param absolute
- * boolean value which specifies if the path is absolute or
- * relative
- *
- * @return A SchemaPath instance.
- */
- public static SchemaNodeIdentifier create(final Iterable<QName> path, final boolean absolute) {
- final SchemaNodeIdentifier parent = absolute ? ROOT : SAME;
- return parent.createChild(path);
- }
-
- /**
- * Constructs new instance of this class with the concrete path.
- *
- * @param absolute
- * boolean value which specifies if the path is absolute or
- * relative
- * @param path
- * one or more QName instances which specifies exact path to the
- * module node
- *
- * @return A SchemaPath instance.
- */
- public static SchemaNodeIdentifier create(final boolean absolute, final QName... path) {
- return create(Arrays.asList(path), absolute);
- }
-
- /**
- * Create a new instance.
- *
- * @param parent Parent schema node identifier
- * @param qname next path element
- * @return A new SchemaPath instance
- */
- protected abstract SchemaNodeIdentifier createInstance(SchemaNodeIdentifier parent, QName qname);
+ public static Descendant of(final QName nodeIdentifier) {
+ return new Descendant(nodeIdentifier);
+ }
- /**
- * Create a child path based on concatenation of this path and a relative path.
- *
- * @param relative Relative path
- * @return A new child path
- */
- public SchemaNodeIdentifier createChild(final Iterable<QName> relative) {
- if (Iterables.isEmpty(relative)) {
- return this;
+ public static Descendant of(final QName... nodeIdentifiers) {
+ return new Descendant(Arrays.asList(nodeIdentifiers));
}
- SchemaNodeIdentifier parentNode = this;
- for (QName qname : relative) {
- parentNode = parentNode.createInstance(parentNode, qname);
+ public static Descendant of(final Collection<QName> nodeIdentifiers) {
+ return new Descendant(nodeIdentifiers);
}
- return parentNode;
+ @Override
+ SchemaPath implicitSchemaPathParent() {
+ return SchemaPath.SAME;
+ }
}
- /**
- * Create a child path based on concatenation of this path and a relative path.
- *
- * @param relative Relative SchemaPath
- * @return A new child path
- */
- public SchemaNodeIdentifier createChild(final SchemaNodeIdentifier relative) {
- Preconditions.checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
+ private static final AtomicReferenceFieldUpdater<SchemaNodeIdentifier, SchemaPath> SCHEMAPATH_UPDATER =
+ AtomicReferenceFieldUpdater.newUpdater(SchemaNodeIdentifier.class, SchemaPath.class, "schemaPath");
- SchemaNodeIdentifier parentNode = this;
- for (QName qname : relative.getPathFromRoot()) {
- parentNode = parentNode.createInstance(parentNode, qname);
- }
+ private final @NonNull Object qnames;
- return parentNode;
- }
+ // Cached SchemaPath.
+ private volatile SchemaPath schemaPath;
+ // Cached hashCode
+ private volatile int hash;
- /**
- * Create a child path based on concatenation of this path and additional
- * path elements.
- *
- * @param elements Relative SchemaPath elements
- * @return A new child path
- */
- public SchemaNodeIdentifier createChild(final QName... elements) {
- return createChild(Arrays.asList(elements));
+ SchemaNodeIdentifier(final QName qname) {
+ this.qnames = requireNonNull(qname);
}
- /**
- * Returns the list of nodes which need to be traversed to get from the
- * starting point (root for absolute SchemaPaths) to the node represented
- * by this object.
- *
- * @return list of <code>qname</code> instances which represents
- * path from the root to the schema node.
- */
- public Iterable<QName> getPathFromRoot() {
- return getLegacyPath();
+ SchemaNodeIdentifier(final Collection<QName> qnames) {
+ final ImmutableList<QName> tmp = ImmutableList.copyOf(qnames);
+ checkArgument(!tmp.isEmpty());
+ this.qnames = tmp.size() == 1 ? tmp.get(0) : tmp;
}
- /**
- * Returns the list of nodes which need to be traversed to get from this
- * node to the starting point (root for absolute SchemaPaths).
- *
- * @return list of <code>qname</code> instances which represents
- * path from the schema node towards the root.
- */
- public Iterable<QName> getPathTowardsRoot() {
- return new Iterable<QName>() {
- @Override
- public Iterator<QName> iterator() {
- return new Iterator<QName>() {
- private SchemaNodeIdentifier current = SchemaNodeIdentifier.this;
-
- @Override
- public boolean hasNext() {
- return current.parent != null;
- }
-
- @Override
- public QName next() {
- if (current.parent != null) {
- final QName ret = current.qname;
- current = current.parent;
- return ret;
- } else {
- throw new NoSuchElementException("No more elements available");
- }
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException("Component removal not supported");
- }
- };
- }
- };
+ public @NonNull List<QName> getNodeIdentifiers() {
+ return qnames instanceof QName ? ImmutableList.of((QName) qnames) : (ImmutableList<QName>) qnames;
}
/**
- * Returns the immediate parent SchemaPath.
+ * Create the {@link SchemaPath} equivalent of this identifier.
*
- * @return Parent path, null if this SchemaPath is already toplevel.
+ * @return SchemaPath equivalent.
*/
- public SchemaNodeIdentifier getParent() {
- return parent;
+ public final @NonNull SchemaPath asSchemaPath() {
+ final SchemaPath ret = schemaPath;
+ return ret != null ? ret : loadSchemaPath();
}
- /**
- * Get the last component of this path.
- *
- * @return The last component of this path.
- */
- public final QName getLastComponent() {
- return qname;
+ private SchemaPath loadSchemaPath() {
+ final SchemaPath newPath = implicitSchemaPathParent().createChild(getNodeIdentifiers());
+ return SCHEMAPATH_UPDATER.compareAndSet(this, null, newPath) ? newPath : schemaPath;
}
- /**
- * Describes whether schema node identifier is|isn't absolute.
- *
- * @return boolean value which is <code>true</code> if schema path is
- * absolute.
- */
- public abstract boolean isAbsolute();
+ abstract SchemaPath implicitSchemaPathParent();
@Override
public final int hashCode() {
- return hash;
+ final int local;
+ return (local = hash) != 0 ? local : (hash = qnames.hashCode());
}
@Override
- public boolean equals(final Object obj) {
+ public final boolean equals(final Object obj) {
if (this == obj) {
return true;
}
- if (obj == null) {
+ if (obj == null || getClass() != obj.getClass()) {
return false;
}
- if (getClass() != obj.getClass()) {
- return false;
- }
- final SchemaNodeIdentifier other = (SchemaNodeIdentifier) obj;
-
- if (qname != null) {
- if (!qname.equals(other.qname)) {
- return false;
- }
- } else {
- if (other.qname != null) {
- return false;
- }
- }
-
- if (parent == null) {
- return other.parent == null;
- }
- return parent.equals(other.parent);
+ return qnames.equals(((SchemaNodeIdentifier) obj).qnames);
}
@Override
public final String toString() {
- return addToStringAttributes(Objects.toStringHelper(this)).toString();
- }
-
- protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
- return toStringHelper.add("path", getPathFromRoot());
+ return MoreObjects.toStringHelper(this).add("qnames", qnames).toString();
}
}