X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-model-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fmodel%2Fapi%2FSchemaPath.java;h=6164a8f891d04179768a769c43d30318216ac5b3;hb=aa0d59e9afecc484e8d0e219d3156e7817266e28;hp=c277d3b8cbb3636c4e93c652ecf6c84a1283feac;hpb=538a97c9989e315f4849ef2265509e49df19198f;p=yangtools.git diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java index c277d3b8cb..6164a8f891 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/SchemaPath.java @@ -7,28 +7,133 @@ */ package org.opendaylight.yangtools.yang.model.api; -import java.util.ArrayList; -import java.util.Collections; +import com.google.common.base.MoreObjects; +import com.google.common.base.MoreObjects.ToStringHelper; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.common.collect.UnmodifiableIterator; +import java.util.Arrays; +import java.util.Iterator; import java.util.List; - +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; +import org.opendaylight.yangtools.concepts.Immutable; import org.opendaylight.yangtools.yang.common.QName; /** - * * Represents unique path to the every node inside the module. - * */ -public class SchemaPath { +public abstract class SchemaPath implements Immutable { + + /** + * An absolute SchemaPath. + */ + private static final class AbsoluteSchemaPath extends SchemaPath { + private AbsoluteSchemaPath(final SchemaPath parent, final QName qname) { + super(parent, qname); + } + + @Override + public boolean isAbsolute() { + return true; + } + + @Override + protected SchemaPath createInstance(final SchemaPath parent, final QName qname) { + return new AbsoluteSchemaPath(parent, qname); + } + } + + /** + * A relative SchemaPath. + */ + private static final class RelativeSchemaPath extends SchemaPath { + private RelativeSchemaPath(final SchemaPath parent, final QName qname) { + super(parent, qname); + } + + @Override + public boolean isAbsolute() { + return false; + } + + @Override + protected SchemaPath createInstance(final SchemaPath parent, final QName qname) { + return new RelativeSchemaPath(parent, qname); + } + } + + @SuppressWarnings("rawtypes") + private static final AtomicReferenceFieldUpdater LEGACYPATH_UPDATER = + AtomicReferenceFieldUpdater.newUpdater(SchemaPath.class, ImmutableList.class, "legacyPath"); + + /** + * Shared instance of the conceptual root schema node. + */ + public static final SchemaPath ROOT = new AbsoluteSchemaPath(null, null); + + /** + * Shared instance of the "same" relative schema node. + */ + public static final SchemaPath SAME = new RelativeSchemaPath(null, null); + + /** + * Parent path. + */ + private final SchemaPath parent; + + /** + * This component. + */ + private final QName qname; + + /** + * Cached hash code. We can use this since we are immutable. + */ + private final int hash; + /** - * List of QName instances which represents complete path to the node. + * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()} + * is invoked. */ - private final List path; + private volatile ImmutableList legacyPath; + + protected SchemaPath(final SchemaPath parent, final QName qname) { + this.parent = parent; + this.qname = qname; + + int h = Objects.hashCode(parent); + if (qname != null) { + h = h * 31 + qname.hashCode(); + } + + hash = h; + } + + private ImmutableList getLegacyPath() { + ImmutableList ret = legacyPath; + if (ret == null) { + ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse(); + LEGACYPATH_UPDATER.lazySet(this, ret); + } + + return ret; + } /** - * Boolean value which represents type of schema path (relative or - * absolute). + * Returns the complete path to schema node. + * + * @return list of QName instances which represents complete + * path to schema node + * + * @deprecated Use {@link #getPathFromRoot()} instead. */ - private final Boolean absolute; + @Deprecated + public List getPath() { + return getLegacyPath(); + } /** * Constructs new instance of this class with the concrete path. @@ -39,20 +144,148 @@ public class SchemaPath { * @param absolute * boolean value which specifies if the path is absolute or * relative + * + * @return A SchemaPath instance. */ - public SchemaPath(final List path, final boolean absolute) { - this.path = Collections.unmodifiableList(new ArrayList(path)); - this.absolute = absolute; + public static SchemaPath create(final Iterable path, final boolean absolute) { + final SchemaPath parent = absolute ? ROOT : SAME; + return parent.createChild(path); } /** - * Returns the complete path to schema node. + * Constructs new instance of this class with the concrete path. * - * @return list of QName instances which represents complete - * path to schema node + * @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 List getPath() { - return path; + public static SchemaPath create(final boolean absolute, final QName... path) { + return create(Arrays.asList(path), absolute); + } + + /** + * Create a new instance. + * + * @param parent Parent SchemaPath + * @param qname next path element + * @return A new SchemaPath instance + */ + protected abstract SchemaPath createInstance(SchemaPath parent, QName qname); + + /** + * Create a child path based on concatenation of this path and a relative path. + * + * @param relative Relative path + * @return A new child path + */ + public SchemaPath createChild(final Iterable relative) { + if (Iterables.isEmpty(relative)) { + return this; + } + + SchemaPath parentPath = this; + for (QName qname : relative) { + parentPath = parentPath.createInstance(parentPath, qname); + } + + return parentPath; + } + + /** + * Create a child path based on concatenation of this path and a relative path. + * + * @param relative Relative SchemaPath + * @return A new child path + */ + public SchemaPath createChild(final SchemaPath relative) { + Preconditions.checkArgument(!relative.isAbsolute(), "Child creation requires relative path"); + + SchemaPath parentPath = this; + for (QName qname : relative.getPathFromRoot()) { + parentPath = parentPath.createInstance(parentPath, qname); + } + + return parentPath; + } + + /** + * 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 SchemaPath createChild(final QName... elements) { + return createChild(Arrays.asList(elements)); + } + + /** + * 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 qname instances which represents + * path from the root to the schema node. + */ + public Iterable getPathFromRoot() { + return getLegacyPath(); + } + + /** + * 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 qname instances which represents + * path from the schema node towards the root. + */ + public Iterable getPathTowardsRoot() { + return new Iterable() { + @Override + public Iterator iterator() { + return new UnmodifiableIterator() { + private SchemaPath current = SchemaPath.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"); + } + } + }; + } + }; + } + + /** + * Returns the immediate parent SchemaPath. + * + * @return Parent path, null if this SchemaPath is already toplevel. + */ + public SchemaPath getParent() { + return parent; + } + + /** + * Get the last component of this path. + * + * @return The last component of this path. + */ + public final QName getLastComponent() { + return qname; } /** @@ -61,17 +294,11 @@ public class SchemaPath { * @return boolean value which is true if schema path is * absolute. */ - public boolean isAbsolute() { - return absolute; - } + public abstract boolean isAbsolute(); @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + absolute.hashCode(); - result = prime * result + ((path == null) ? 0 : path.hashCode()); - return result; + public final int hashCode() { + return hash; } @Override @@ -85,28 +312,30 @@ public class SchemaPath { if (getClass() != obj.getClass()) { return false; } - SchemaPath other = (SchemaPath) obj; - if (absolute != other.absolute) { - return false; - } - if (path == null) { - if (other.path != null) { + final SchemaPath other = (SchemaPath) obj; + + if (qname != null) { + if (!qname.equals(other.qname)) { + return false; + } + } else { + if (other.qname != null) { return false; } - } else if (!path.equals(other.path)) { - return false; } - return true; + + if (parent == null) { + return other.parent == null; + } + return parent.equals(other.parent); } @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append("SchemaPath [path="); - builder.append(path); - builder.append(", absolute="); - builder.append(absolute); - builder.append("]"); - return builder.toString(); + public final String toString() { + return addToStringAttributes(MoreObjects.toStringHelper(this)).toString(); + } + + protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) { + return toStringHelper.add("path", getPathFromRoot()); } }