Use Objects.hashCode()
[yangtools.git] / yang / yang-model-api / src / main / java / org / opendaylight / yangtools / yang / model / api / SchemaPath.java
index 882d48e2377e724e7610b0d3167251ac29e08c46..6164a8f891d04179768a769c43d30318216ac5b3 100644 (file)
@@ -7,44 +7,87 @@
  */
 package org.opendaylight.yangtools.yang.model.api;
 
+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.Collections;
+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.util.HashCodeBuilder;
 import org.opendaylight.yangtools.yang.common.QName;
 
 /**
- *
  * Represents unique path to the every node inside the module.
- *
  */
-public class SchemaPath implements Immutable {
+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<SchemaPath, ImmutableList> LEGACYPATH_UPDATER =
+            AtomicReferenceFieldUpdater.newUpdater(SchemaPath.class, ImmutableList.class, "legacyPath");
+
     /**
      * Shared instance of the conceptual root schema node.
      */
-    public static final SchemaPath ROOT = new SchemaPath(Collections.<QName>emptyList(), true, Boolean.TRUE.hashCode());
+    public static final SchemaPath ROOT = new AbsoluteSchemaPath(null, null);
 
     /**
      * Shared instance of the "same" relative schema node.
      */
-    public static final SchemaPath SAME = new SchemaPath(Collections.<QName>emptyList(), false, Boolean.FALSE.hashCode());
+    public static final SchemaPath SAME = new RelativeSchemaPath(null, null);
 
     /**
-     * List of QName instances which represents complete path to the node.
+     * Parent path.
      */
-    private final Iterable<QName> path;
+    private final SchemaPath parent;
 
     /**
-     * Boolean value which represents type of schema path (relative or
-     * absolute).
+     * This component.
      */
-    private final boolean absolute;
+    private final QName qname;
 
     /**
      * Cached hash code. We can use this since we are immutable.
@@ -55,14 +98,28 @@ public class SchemaPath implements Immutable {
      * Cached legacy path, filled-in when {@link #getPath()} or {@link #getPathTowardsRoot()}
      * is invoked.
      */
-    private ImmutableList<QName> legacyPath;
+    private volatile ImmutableList<QName> 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<QName> getLegacyPath() {
-        if (legacyPath == null) {
-            legacyPath = ImmutableList.copyOf(path);
+        ImmutableList<QName> ret = legacyPath;
+        if (ret == null) {
+            ret = ImmutableList.copyOf(getPathTowardsRoot()).reverse();
+            LEGACYPATH_UPDATER.lazySet(this, ret);
         }
 
-        return legacyPath;
+        return ret;
     }
 
     /**
@@ -78,12 +135,6 @@ public class SchemaPath implements Immutable {
         return getLegacyPath();
     }
 
-    private SchemaPath(final Iterable<QName> path, final boolean absolute, final int hash) {
-        this.path = Preconditions.checkNotNull(path);
-        this.absolute = absolute;
-        this.hash = hash;
-    }
-
     /**
      * Constructs new instance of this class with the concrete path.
      *
@@ -117,18 +168,14 @@ public class SchemaPath implements Immutable {
         return create(Arrays.asList(path), absolute);
     }
 
-    private SchemaPath trustedCreateChild(final Iterable<QName> relative) {
-        if (Iterables.isEmpty(relative)) {
-            return this;
-        }
-
-        final HashCodeBuilder<QName> b = new HashCodeBuilder<>(hash);
-        for (QName p : relative) {
-            b.addArgument(p);
-        }
-
-        return new SchemaPath(Iterables.concat(path, relative), absolute, b.toInstance());
-    }
+    /**
+     * 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.
@@ -141,7 +188,12 @@ public class SchemaPath implements Immutable {
             return this;
         }
 
-        return trustedCreateChild(ImmutableList.copyOf(relative));
+        SchemaPath parentPath = this;
+        for (QName qname : relative) {
+            parentPath = parentPath.createInstance(parentPath, qname);
+        }
+
+        return parentPath;
     }
 
     /**
@@ -152,7 +204,13 @@ public class SchemaPath implements Immutable {
      */
     public SchemaPath createChild(final SchemaPath relative) {
         Preconditions.checkArgument(!relative.isAbsolute(), "Child creation requires relative path");
-        return trustedCreateChild(relative.path);
+
+        SchemaPath parentPath = this;
+        for (QName qname : relative.getPathFromRoot()) {
+            parentPath = parentPath.createInstance(parentPath, qname);
+        }
+
+        return parentPath;
     }
 
     /**
@@ -175,7 +233,7 @@ public class SchemaPath implements Immutable {
      *         path from the root to the schema node.
      */
     public Iterable<QName> getPathFromRoot() {
-        return path;
+        return getLegacyPath();
     }
 
     /**
@@ -186,7 +244,30 @@ public class SchemaPath implements Immutable {
      *         path from the schema node towards the root.
      */
     public Iterable<QName> getPathTowardsRoot() {
-        return getLegacyPath().reverse();
+        return new Iterable<QName>() {
+            @Override
+            public Iterator<QName> iterator() {
+                return new UnmodifiableIterator<QName>() {
+                    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");
+                        }
+                    }
+                };
+            }
+        };
     }
 
     /**
@@ -195,13 +276,16 @@ public class SchemaPath implements Immutable {
      * @return Parent path, null if this SchemaPath is already toplevel.
      */
     public SchemaPath getParent() {
-        final int size = Iterables.size(path);
-        if (size != 0) {
-            final SchemaPath parent = isAbsolute() ? ROOT : SAME;
-            return parent.trustedCreateChild(Iterables.limit(path, size - 1));
-        } else {
-            return null;
-        }
+        return parent;
+    }
+
+    /**
+     * Get the last component of this path.
+     *
+     * @return The last component of this path.
+     */
+    public final QName getLastComponent() {
+        return qname;
     }
 
     /**
@@ -210,12 +294,10 @@ public class SchemaPath implements Immutable {
      * @return boolean value which is <code>true</code> if schema path is
      *         absolute.
      */
-    public boolean isAbsolute() {
-        return absolute;
-    }
+    public abstract boolean isAbsolute();
 
     @Override
-    public int hashCode() {
+    public final int hashCode() {
         return hash;
     }
 
@@ -230,22 +312,30 @@ public class SchemaPath implements Immutable {
         if (getClass() != obj.getClass()) {
             return false;
         }
-        SchemaPath other = (SchemaPath) obj;
-        if (absolute != other.absolute) {
-            return false;
+        final SchemaPath other = (SchemaPath) obj;
+
+        if (qname != null) {
+            if (!qname.equals(other.qname)) {
+                return false;
+            }
+        } else {
+            if (other.qname != null) {
+                return false;
+            }
         }
 
-        return Iterables.elementsEqual(path, other.path);
+        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());
     }
 }