BUG-592: Rework instance identifier 78/5778/6
authorRobert Varga <rovarga@cisco.com>
Wed, 26 Mar 2014 14:18:50 +0000 (15:18 +0100)
committerTony Tkacik <ttkacik@cisco.com>
Wed, 16 Apr 2014 13:57:56 +0000 (13:57 +0000)
This patch reworks the way InstanceIdentifier is implemented, gearing it
up for performance.

The entire implementation is based on Iterables instead of Lists, which
allows us to avoid copying them over and over as we construct new
identifiers. Simple .child() operation is a lot quicker, saving
instantiation of two intermediate classes.

The implementation gets rid of constructors, such that there is no
direct construction, but rather all paths are validated for content.
This also acts as a gate-keeper, enforcing the immutable nature of the
identifier.

Another optimization is that the hashCode() value is now built
incrementally as more members are added and cached.

InstanceIdentifierBuilder, now a full class, does not explictly copy the
base identifier, but reuses it -- there is no reason not to, as the base
identifier is immutable.

Change-Id: I0826ed3169a107c8f4b6e3026569e87387305ce0
Signed-off-by: Robert Varga <rovarga@cisco.com>
code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/InstanceIdentifierCodecImpl.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/HashCodeBuilder.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifierBuilderImpl.java [new file with mode: 0644]
yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/KeyedInstanceIdentifier.java [new file with mode: 0644]
yang/yang-binding/src/test/java/org/opendaylight/yangtools/yang/binding/test/InstanceIdentifierTest.java

index 0d91bd9551c93be8a672c1a223bf68e03983ba9e..6dbb0f271aecadc302afe7f9dc132a9284aaabf5 100644 (file)
@@ -80,7 +80,7 @@ public class InstanceIdentifierCodecImpl implements InstanceIdentifierCodec {
             }
             baArgs.add(baArg);
         }
-        InstanceIdentifier ret = new InstanceIdentifier(baArgs, (Class<? extends DataObject>) baType);
+        InstanceIdentifier ret = InstanceIdentifier.create(baArgs);
         LOG.debug("DOM Instance Identifier {} deserialized to {}", input, ret);
         return ret;
     }
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/HashCodeBuilder.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/HashCodeBuilder.java
new file mode 100644 (file)
index 0000000..db882a0
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+import org.opendaylight.yangtools.concepts.Builder;
+
+final class HashCodeBuilder<T> implements Builder<Integer> {
+    private int currentHash;
+
+    public HashCodeBuilder() {
+        this(1);
+    }
+
+    public HashCodeBuilder(final int seedHash) {
+        this.currentHash = seedHash;
+    }
+
+    public static int nextHashCode(final int hashCode, final Object arg) {
+        return 31 * hashCode + arg.hashCode();
+    }
+
+    void addArgument(final T arg) {
+        currentHash = nextHashCode(currentHash, arg);
+    }
+
+    @Override
+    public Integer toInstance() {
+        return currentHash;
+    }
+}
index ff75bd1d0ef99193dcd525aff93c22024d4d2202..04f614da9385bd40709b75eadf2f648f4913503b 100644 (file)
@@ -8,12 +8,17 @@
 package org.opendaylight.yangtools.yang.binding;
 
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.List;
 
 import org.opendaylight.yangtools.concepts.Builder;
 import org.opendaylight.yangtools.concepts.Immutable;
 import org.opendaylight.yangtools.concepts.Path;
 
+import com.google.common.base.Objects;
+import com.google.common.base.Objects.ToStringHelper;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterables;
 
@@ -46,71 +51,90 @@ import com.google.common.collect.Iterables;
  * This would be the same as using a path like so, "/nodes/node/openflow:1" to refer to the openflow:1 node
  *
  */
-public final class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>,Immutable {
-
-    private final List<PathArgument> path;
+public class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>, Immutable {
+    /*
+     * Protected to differentiate internal and external access. Internal
+     * access is required never to modify the contents. References passed
+     * to outside entities have to be wrapped in an unmodifiable view.
+     */
+    protected final Iterable<PathArgument> pathArguments;
     private final Class<T> targetType;
+    private final boolean wildcarded;
+    private final int hash;
+
+    InstanceIdentifier(final Class<T> type, final Iterable<PathArgument> pathArguments, final boolean wildcarded, final int hash) {
+        this.pathArguments = Preconditions.checkNotNull(pathArguments);
+        this.targetType = Preconditions.checkNotNull(type);
+        this.wildcarded = wildcarded;
+        this.hash = hash;
+    }
 
     /**
-     * Create an instance identifier for a very specific object type.
-     *
-     * For example
-     * <pre>
-     *      new InstanceIdentifier(Nodes.class)
-     * </pre>
-     * would create an InstanceIdentifier for an object of type Nodes
+     * Return the type of data which this InstanceIdentifier identifies.
      *
-     * @param type The type of the object which this instance identifier represents
+     * @return Target type
      */
-    public InstanceIdentifier(Class<T> type) {
-        this(Collections.<PathArgument> singletonList(new Item<>(type)), type);
+    public final Class<T> getTargetType() {
+        return targetType;
     }
 
     /**
-     * Create an instance identifier for a very specific object type.
+     * Return the path argument chain which makes up this instance identifier.
      *
-     * Example
-     * <pre>
-     *  List<PathArgument> path = Arrays.asList(new Item(Nodes.class))
-     *  new InstanceIdentifier(path, Nodes.class);
-     * </pre>
-     *
-     * @param path The path to a specific node in the data tree
-     * @param type The type of the object which this instance identifier represents
+     * @return Path argument chain. Immutable and does not contain nulls.
      */
-    public InstanceIdentifier(List<PathArgument> path, Class<T> type) {
-        this.path = ImmutableList.copyOf(path);
-        this.targetType = type;
+    public final Iterable<PathArgument> getPathArguments() {
+        return Iterables.unmodifiableIterable(pathArguments);
     }
 
     /**
+     * Check whether an instance identifier contains any wildcards. A wildcard
+     * is an path argument which has a null key.
      *
-     * @return A list of the elements of the path
+     * @return @true if any of the path arguments has a null key.
      */
-    public List<PathArgument> getPath() {
-        return getPathArguments();
+    public final boolean isWildcarded() {
+        return wildcarded;
     }
 
-    /**
-     *
-     * @return A list of the elements of the path
-     */
+    @Override
+    public final int hashCode() {
+        return hash;
+    }
 
-    public List<PathArgument> getPathArguments() {
-        return this.path;
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+
+        final InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
+        if (hash != other.hash) {
+            return false;
+        }
+
+        return Iterables.elementsEqual(pathArguments, other.pathArguments);
+    }
+
+    @Override
+    public final String toString() {
+        return addToStringAttributes(Objects.toStringHelper(this)).toString();
     }
 
     /**
+     * Add class-specific toString attributes.
      *
-     * @return The target type of this instance identifier
+     * @param toStringHelper ToStringHelper instance
+     * @return ToStringHelper instance which was passed in
      */
-    public Class<T> getTargetType() {
-        return this.targetType;
-    }
-
-    @Override
-    public String toString() {
-        return "InstanceIdentifier [path=" + path + "]";
+    protected ToStringHelper addToStringAttributes(final ToStringHelper toStringHelper) {
+        return toStringHelper.add("targetType", targetType).add("path", Iterables.toString(getPathArguments()));
     }
 
     /**
@@ -132,12 +156,13 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
      * @return trimmed instance identifier, or null if the component type
      *         is not present.
      */
-    @SuppressWarnings("hiding")
-    public <T extends DataObject> InstanceIdentifier<T> firstIdentifierOf(final Class<T> type) {
+    public final <I extends DataObject> InstanceIdentifier<I> firstIdentifierOf(final Class<I> type) {
         int i = 1;
-        for (final PathArgument a : path) {
+        for (final PathArgument a : getPathArguments()) {
             if (type.equals(a.getType())) {
-                return new InstanceIdentifier<>(path.subList(0, i), type);
+                @SuppressWarnings("unchecked")
+                final InstanceIdentifier<I> ret = (InstanceIdentifier<I>) create(Iterables.limit(getPathArguments(), i));
+                return ret;
             }
 
             ++i;
@@ -155,8 +180,8 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
      * @return key associated with the component, or null if the component type
      *         is not present.
      */
-    public <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem, final Class<K> listKey) {
-        for (PathArgument i : path) {
+    public final <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K firstKeyOf(final Class<N> listItem, final Class<K> listKey) {
+        for (final PathArgument i : getPathArguments()) {
             if (listItem.equals(i.getType())) {
                 @SuppressWarnings("unchecked")
                 final K ret = ((IdentifiableItem<N, K>)i).getKey();
@@ -167,6 +192,226 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
         return null;
     }
 
+    /**
+     * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all
+     * PathArguments to match exactly, e.g.
+     *
+     *
+     * The contains method checks if the other identifier is fully contained within the current identifier. It does this
+     * by looking at only the types of the path arguments and not by comparing the path arguments themselves.
+     *
+     * To illustrate here is an example which explains the working of this API.
+     *
+     * Let's say you have two instance identifiers as follows,
+     *
+     * this = /nodes/node/openflow:1
+     * other = /nodes/node/openflow:2
+     *
+     * then this.contains(other) will return false.
+     *
+     * @param other
+     * @return
+     */
+    @Override
+    public final boolean contains(final InstanceIdentifier<? extends DataObject> other) {
+        Preconditions.checkNotNull(other, "other should not be null");
+
+        final Iterator<?> lit = pathArguments.iterator();
+        final Iterator<?> oit = other.pathArguments.iterator();
+
+        while (lit.hasNext()) {
+            if (!oit.hasNext()) {
+                return false;
+            }
+
+            if (!lit.next().equals(oit.next())) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar
+     * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their
+     * non-wildcarded PathArgument counterpart.
+     *
+     * @param other Identifier which should be checked for inclusion.
+     * @return @true if this identifier contains the other object
+     */
+    public final boolean containsWildcarded(final InstanceIdentifier<?> other) {
+        Preconditions.checkNotNull(other, "other should not be null");
+
+        final Iterator<PathArgument> lit = pathArguments.iterator();
+        final Iterator<PathArgument> oit = other.pathArguments.iterator();
+
+        while (lit.hasNext()) {
+            if (!oit.hasNext()) {
+                return false;
+            }
+
+            final PathArgument la = lit.next();
+            final PathArgument oa = oit.next();
+
+            if (!la.getType().equals(oa.getType())) {
+                return false;
+            }
+            if (la instanceof IdentifiableItem<?, ?> && oa instanceof IdentifiableItem<?, ?> && !la.equals(oa)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Create a builder rooted at this key.
+     *
+     * @return A builder instance
+     */
+    public InstanceIdentifierBuilder<T> builder() {
+        return new InstanceIdentifierBuilderImpl<T>(new Item<T>(targetType), pathArguments, hash, isWildcarded());
+    }
+
+    private InstanceIdentifier<?> childIdentifier(final PathArgument arg) {
+        return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)), HashCodeBuilder.nextHashCode(hash, arg), isWildcarded());
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <N extends ChildOf<? super T>> InstanceIdentifier<N> child(final Class<N> container) {
+        final PathArgument arg = new Item<>(container);
+        return (InstanceIdentifier<N>) childIdentifier(arg);
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifier<N> child(
+            final Class<N> listItem, final K listKey) {
+        final PathArgument arg = new IdentifiableItem<>(listItem, listKey);
+        return (InstanceIdentifier<N>) childIdentifier(arg);
+    }
+
+    @SuppressWarnings("unchecked")
+    public final <N extends DataObject & Augmentation<? super T>> InstanceIdentifier<N> augmentation(
+            final Class<N> container) {
+        final PathArgument arg = new Item<>(container);
+        return (InstanceIdentifier<N>) childIdentifier(arg);
+    }
+
+    @Deprecated
+    private List<PathArgument> legacyCache;
+
+    /**
+     * @deprecated Use {@link #getPathArguments()} instead.
+     */
+    @Deprecated
+    public final List<PathArgument> getPath() {
+        if (legacyCache == null) {
+            legacyCache = ImmutableList.<PathArgument>copyOf(getPathArguments());
+        }
+
+        return legacyCache;
+    }
+
+    /**
+     * Create a new InstanceIdentifierBuilder given a base InstanceIdentifier
+     *
+     * @param basePath
+     * @param <T>
+     * @return
+     *
+     * @deprecated Use {@link #builder()} instead.
+     */
+    @Deprecated
+    public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(final InstanceIdentifier<T> base) {
+        return base.builder();
+    }
+
+    /**
+     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container
+     *
+     * @param container
+     * @param <T>
+     * @return
+     */
+    public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(final Class<T> container) {
+        return new InstanceIdentifierBuilderImpl<T>().addNode(container);
+    }
+
+    /**
+     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an IdentifiableItem
+     *
+     * @param listItem
+     * @param listKey
+     * @param <N>
+     * @param <K>
+     * @return
+     */
+    public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
+            final Class<N> listItem, final K listKey) {
+        return new InstanceIdentifierBuilderImpl<N>().addNode(listItem, listKey);
+    }
+
+    /**
+     * Create an instance identifier for a very specific object type.
+     *
+     * Example
+     * <pre>
+     *  List<PathArgument> path = Arrays.asList(new Item(Nodes.class))
+     *  new InstanceIdentifier(path);
+     * </pre>
+     *
+     * @param pathArguments The path to a specific node in the data tree
+     * @return InstanceIdentifier instance
+     * @throws IllegalArgumentException if pathArguments is empty or
+     *         contains a null element.
+     */
+    public static InstanceIdentifier<?> create(final Iterable<? extends PathArgument> pathArguments) {
+        final Iterator<? extends PathArgument> it = Preconditions.checkNotNull(pathArguments, "pathArguments may not be null").iterator();
+        final HashCodeBuilder<PathArgument> hashBuilder = new HashCodeBuilder<>();
+        boolean wildcard = false;
+        PathArgument a = null;
+
+        while (it.hasNext()) {
+            a = it.next();
+            Preconditions.checkArgument(a != null, "pathArguments may not contain null elements");
+
+            // TODO: sanity check ChildOf<>;
+            hashBuilder.addArgument(a);
+
+            if (Identifiable.class.isAssignableFrom(a.getType()) && !(a instanceof IdentifiableItem<?, ?>)) {
+                wildcard = true;
+            }
+        }
+        Preconditions.checkArgument(a != null, "pathArguments may not be empty");
+
+        final Iterable<PathArgument> immutableArguments;
+        if (pathArguments instanceof ImmutableCollection<?>) {
+            immutableArguments = (Iterable<PathArgument>) pathArguments;
+        } else {
+            immutableArguments = ImmutableList.copyOf(pathArguments);
+        }
+
+        return trustedCreate(a, immutableArguments, hashBuilder.toInstance(), wildcard);
+    }
+
+    /**
+     * Create an instance identifier for a very specific object type.
+     *
+     * For example
+     * <pre>
+     *      new InstanceIdentifier(Nodes.class)
+     * </pre>
+     * would create an InstanceIdentifier for an object of type Nodes
+     *
+     * @param type The type of the object which this instance identifier represents
+     * @return InstanceIdentifier instance
+     */
+    @SuppressWarnings("unchecked")
+    public static <T extends DataObject> InstanceIdentifier<T> create(final Class<T> type) {
+        return (InstanceIdentifier<T>) create(Collections.<PathArgument> singletonList(new Item<>(type)));
+    }
+
     /**
      * Return the key associated with the last component of the specified identifier.
      *
@@ -175,38 +420,45 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
      */
     public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
         @SuppressWarnings("unchecked")
-        final K ret = ((IdentifiableItem<N, K>)Iterables.getLast(id.getPath())).getKey();
+        final K ret = ((KeyedInstanceIdentifier<N, K>)id).getKey();
         return ret;
     }
 
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    static InstanceIdentifier<?> trustedCreate(final PathArgument arg, final Iterable<PathArgument> pathArguments, final int hash, boolean wildcarded) {
+        if (Identifiable.class.isAssignableFrom(arg.getType())) {
+            Identifier<?> key = null;
+            if (arg instanceof IdentifiableItem<?, ?>) {
+                key = ((IdentifiableItem<?, ?>)arg).key;
+            } else {
+                wildcarded = true;
+            }
+
+            return new KeyedInstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash, key);
+        } else {
+            return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash);
+        }
+    }
+
     /**
      * Path argument of {@link InstanceIdentifier}.
      * <p>
      * Interface which implementations are used as path components of the
      * path in overall data tree.
-     *
      */
     public interface PathArgument {
-
         Class<? extends DataObject> getType();
     }
 
-
-    /**
-     * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
-     * a kind. In YANG terms this would probably represent a container.
-     *
-     * @param <T>
-     */
-    public static final class Item<T extends DataObject> implements PathArgument {
+    private static abstract class AbstractPathArgument<T extends DataObject> implements PathArgument {
         private final Class<T> type;
 
-        public Item(Class<T> type) {
-            this.type = type;
+        protected AbstractPathArgument(final Class<T> type) {
+            this.type = Preconditions.checkNotNull(type, "Type may not be null.");
         }
 
         @Override
-        public Class<T> getType() {
+        public final Class<T> getType() {
             return type;
         }
 
@@ -219,25 +471,42 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
         }
 
         @Override
-        public boolean equals(Object obj) {
-            if (this == obj)
+        public boolean equals(final Object obj) {
+            if (this == obj) {
                 return true;
-            if (obj == null)
+            }
+            if (obj == null) {
                 return false;
-            if (getClass() != obj.getClass())
+            }
+            if (getClass() != obj.getClass()) {
                 return false;
-            Item<?> other = (Item<?>) obj;
+            }
+            final AbstractPathArgument<?> other = (AbstractPathArgument<?>) obj;
             if (type == null) {
-                if (other.type != null)
+                if (other.type != null) {
                     return false;
-            } else if (!type.equals(other.type))
+                }
+            } else if (!type.equals(other.type)) {
                 return false;
+            }
             return true;
         }
+    }
+
+    /**
+     * An Item represents an object that probably is only one of it's kind. For example a Nodes object is only one of
+     * a kind. In YANG terms this would probably represent a container.
+     *
+     * @param <T>
+     */
+    public static final class Item<T extends DataObject> extends AbstractPathArgument<T> {
+        public Item(final Class<T> type) {
+            super(type);
+        }
 
         @Override
         public String toString() {
-            return type.getName();
+            return getType().getName();
         }
     }
 
@@ -248,19 +517,12 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
      * @param <I> An object that is identifiable by an identifier
      * @param <T> The identifier of the object
      */
-    public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> implements
-            PathArgument {
-
+    public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> extends AbstractPathArgument<I> {
         private final T key;
-        private final Class<I> type;
-
-        public IdentifiableItem(Class<I> type, T key) {
-            if (type == null)
-                throw new IllegalArgumentException("Type must not be null.");
-            if (key == null)
-                throw new IllegalArgumentException("Key must not be null.");
-            this.type = type;
-            this.key = key;
+
+        public IdentifiableItem(final Class<I> type, final T key) {
+            super(type);
+            this.key = Preconditions.checkNotNull(key, "Key may not be null.");
         }
 
         public T getKey() {
@@ -268,23 +530,8 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
         }
 
         @Override
-        public Class<I> getType() {
-            return this.type;
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj == null) {
-                return false;
-            }
-            if (obj.hashCode() != hashCode()) {
-                return false;
-            }
-            if (!(obj instanceof IdentifiableItem<?, ?>)) {
-                return false;
-            }
-            IdentifiableItem<?, ?> foreign = (IdentifiableItem<?, ?>) obj;
-            return key.equals(foreign.getKey());
+        public boolean equals(final Object obj) {
+            return super.equals(obj) && obj.hashCode() == hashCode() && key.equals(((IdentifiableItem<?, ?>) obj).getKey());
         }
 
         @Override
@@ -294,24 +541,12 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
 
         @Override
         public String toString() {
-            return type.getName() + "[key=" + key + "]";
+            return getType().getName() + "[key=" + key + "]";
         }
     }
 
-    public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
-        /**
-         * @deprecated use {@link child(Class)} or {@link augmentation(Class)} instead.
-         */
-        @Deprecated
-        <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container);
-
-        /**
-         * @deprecated use {@link child(Class,Identifier)} or {@link augmentation(Class,Identifier)} instead.
-         */
-        @Deprecated
-        <N extends Identifiable<K> & DataObject, K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
-                Class<N> listItem, K listKey);
 
+    public interface InstanceIdentifierBuilder<T extends DataObject> extends Builder<InstanceIdentifier<T>> {
         /**
          * Append the specified container as a child of the current InstanceIdentifier referenced by the builder.
          *
@@ -331,7 +566,8 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
          * @param <N>
          * @return
          */
-        <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container);
+        <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(
+                Class<N> container);
 
         /**
          * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder.
@@ -356,7 +592,8 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
          * @param <N>
          * @return
          */
-        <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(Class<N> container);
+        <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(
+                Class<N> container);
 
         /**
          * Build the instance identifier.
@@ -364,232 +601,5 @@ public final class InstanceIdentifier<T extends DataObject> implements Path<Inst
          * @return
          */
         InstanceIdentifier<T> build();
-
-    }
-
-    /**
-     * @deprecated use {@link builder(Class)} or {@link builder(Class,Identifier)} instead.
-     */
-    @Deprecated
-    @SuppressWarnings("rawtypes")
-    public static InstanceIdentifierBuilder<?> builder() {
-        return new BuilderImpl();
-    }
-
-    /**
-     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container
-     *
-     * @param container
-     * @param <T>
-     * @return
-     */
-    public static <T extends ChildOf<? extends DataRoot>> InstanceIdentifierBuilder<T> builder(Class<T> container) {
-        return new BuilderImpl<T>().addNode(container);
-    }
-
-    /**
-     * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an IdentifiableItem
-     *
-     * @param listItem
-     * @param listKey
-     * @param <N>
-     * @param <K>
-     * @return
-     */
-    public static <N extends Identifiable<K> & ChildOf<? extends DataRoot>, K extends Identifier<N>> InstanceIdentifierBuilder<N> builder(
-            Class<N> listItem, K listKey) {
-        return new BuilderImpl<N>().addNode(listItem, listKey);
-    }
-
-    /**
-     * Create a new InstanceIdentifierBuilder given a base InstanceIdentifier
-     *
-     * @param basePath
-     * @param <T>
-     * @return
-     */
-    public static <T extends DataObject> InstanceIdentifierBuilder<T> builder(InstanceIdentifier<T> basePath) {
-        return new BuilderImpl<T>(basePath.path,basePath.targetType);
-    }
-
-    private static final class BuilderImpl<T extends DataObject> implements InstanceIdentifierBuilder<T> {
-
-        private final ImmutableList.Builder<PathArgument> path;
-        private Class<? extends DataObject> target = null;
-
-        public BuilderImpl() {
-            this.path = ImmutableList.builder();
-        }
-
-        public BuilderImpl(List<? extends PathArgument> prefix,Class<? extends DataObject> target) {
-            this.path = ImmutableList.<PathArgument>builder().addAll(prefix);
-            this.target = target;
-        }
-
-        @SuppressWarnings("unchecked")
-        private <N extends DataObject> InstanceIdentifierBuilder<N> addNode(Class<N> container) {
-            target = container;
-            path.add(new Item<N>(container));
-            return (InstanceIdentifierBuilder<N>) this;
-        }
-
-        @SuppressWarnings("unchecked")
-        private <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> addNode(
-                Class<N> listItem, K listKey) {
-            target = listItem;
-            path.add(new IdentifiableItem<N, K>(listItem, listKey));
-            return (InstanceIdentifierBuilder<N>) this;
-        }
-
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        @Override
-        public InstanceIdentifier<T> toInstance() {
-            return new InstanceIdentifier(path.build(), target);
-        }
-
-        @Override
-        public InstanceIdentifier<T> build() {
-            return toInstance();
-        }
-
-        @Override
-        public <N extends DataObject> InstanceIdentifierBuilder<N> node(Class<N> container) {
-            return addNode(container);
-        }
-
-        @Override
-        public <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilder<N> node(
-                Class<N> listItem, K listKey) {
-            return addNode(listItem, listKey);
-        }
-
-        @Override
-        public <N extends ChildOf<? super T>> InstanceIdentifierBuilder<N> child(Class<N> container) {
-            return addNode(container);
-        }
-
-        @Override
-        public <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilder<N> child(
-                Class<N> listItem, K listKey) {
-            return addNode(listItem,listKey);
-        }
-
-        @Override
-        public <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilder<N> augmentation(
-                Class<N> container) {
-            return addNode(container);
-        }
-
-        @Override
-        public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((path == null) ? 0 : path.hashCode());
-            return result;
-        }
-    }
-
-    @Override
-    public int hashCode() {
-        final int prime = 31;
-        int result = 1;
-        result = prime * result + ((path == null) ? 0 : path.hashCode());
-        return result;
-    }
-
-    @Override
-    public boolean equals(Object obj) {
-        if (this == obj) {
-            return true;
-        }
-        if (obj == null) {
-            return false;
-        }
-        if (getClass() != obj.getClass()) {
-            return false;
-        }
-        InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
-        if (path == null) {
-            if (other.path != null) {
-                return false;
-            }
-        } else if (!path.equals(other.path)) {
-            return false;
-        }
-        return true;
-    }
-
-    /**
-     * The contains method checks if the other identifier is fully contained within the current identifier. It does this
-     * by looking at only the types of the path arguments and not by comparing the path arguments themselse.
-     * If you want to compare path arguments you must use containsWildcarded
-     *
-     * To illustrate here is an example which explains the working of this api.
-     *
-     * Let's say you have two instance identifiers as follows,
-     *
-     * this = /nodes/node/openflow:1
-     * other = /nodes/node/openflow:2
-     *
-     * then this.contains(other) will return true. To ensure that this and other are compared properly you must use
-     * containsWildcarded
-     *
-     * @param other
-     * @return
-     */
-    @Override
-    public boolean contains(final InstanceIdentifier<?> other) {
-        if(other == null) {
-            throw new IllegalArgumentException("other should not be null");
-        }
-        final int localSize = this.path.size();
-        final List<PathArgument> otherPath = other.getPath();
-        if(localSize > other.path.size()) {
-            return false;
-        }
-        for(int i = 0;i<localSize;i++ ) {
-            if(!path.get(i).equals(otherPath.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    /**
-     * The containsWildcarded method checks if the other identifier is fully contained within the current identifier.
-     * It does this by looking at both the type and identity of the path arguments.
-     *
-     * @param other
-     * @return
-     */
-    public boolean containsWildcarded(final InstanceIdentifier<?> other) {
-        if(other == null) {
-            throw new IllegalArgumentException("other should not be null");
-        }
-        final int localSize = this.path.size();
-        final List<PathArgument> otherPath = other.getPath();
-        if(localSize > other.path.size()) {
-            return false;
-        }
-        for(int i = 0;i<localSize;i++ ) {
-            final PathArgument localArgument = path.get(i);
-            final PathArgument otherArgument = otherPath.get(i);
-            if(!localArgument.getType().equals(otherArgument.getType())) {
-                return false;
-            }
-            if(localArgument instanceof IdentifiableItem<?, ?> && otherArgument instanceof IdentifiableItem<?, ?> && !localArgument.equals(otherPath.get(i))) {
-                return false;
-            }
-        }
-        return true;
-    }
-
-    public boolean isWildcarded() {
-        for(PathArgument pathArgument : path) {
-            if(Identifiable.class.isAssignableFrom(pathArgument.getType()) && !(pathArgument instanceof IdentifiableItem<?, ?>)) {
-                return true;
-            }
-        }
-        return false;
     }
 }
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifierBuilderImpl.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifierBuilderImpl.java
new file mode 100644 (file)
index 0000000..bdee435
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.IdentifiableItem;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
+
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Iterables;
+
+final class InstanceIdentifierBuilderImpl<T extends DataObject> implements InstanceIdentifier.InstanceIdentifierBuilder<T> {
+    private final ImmutableList.Builder<PathArgument> pathBuilder = ImmutableList.builder();
+    private final HashCodeBuilder<PathArgument> hashBuilder;
+    private final Iterable<? extends PathArgument> basePath;
+    private boolean wildcard = false;
+    private PathArgument arg = null;
+
+    InstanceIdentifierBuilderImpl() {
+        this.hashBuilder = new HashCodeBuilder<>();
+        this.basePath = null;
+    }
+
+    InstanceIdentifierBuilderImpl(final PathArgument item, final Iterable<? extends PathArgument> pathArguments, final int hash, final boolean wildcard) {
+        this.hashBuilder = new HashCodeBuilder<>(hash);
+        this.basePath = pathArguments;
+        this.wildcard = wildcard;
+        this.arg = item;
+    }
+
+    @Override
+    public int hashCode() {
+        return hashBuilder.toInstance();
+    }
+
+    @SuppressWarnings("unchecked")
+    <N extends DataObject> InstanceIdentifierBuilderImpl<N> addNode(final Class<N> container) {
+        arg = new Item<N>(container);
+        hashBuilder.addArgument(arg);
+        pathBuilder.add(arg);
+
+        if (Identifiable.class.isAssignableFrom(container)) {
+            wildcard = true;
+        }
+
+        return (InstanceIdentifierBuilderImpl<N>) this;
+    }
+
+    @SuppressWarnings("unchecked")
+    <N extends DataObject & Identifiable<K> , K extends Identifier<N>> InstanceIdentifierBuilderImpl<N> addNode(final Class<N> listItem, final K listKey) {
+        arg = new IdentifiableItem<N, K>(listItem, listKey);
+        hashBuilder.addArgument(arg);
+        pathBuilder.add(arg);
+        return (InstanceIdentifierBuilderImpl<N>) this;
+    }
+
+    @Override
+    public <N extends ChildOf<? super T>> InstanceIdentifierBuilderImpl<N> child(final Class<N> container) {
+        return addNode(container);
+    }
+
+    @Override
+    public <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifierBuilderImpl<N> child(final Class<N> listItem, final K listKey) {
+        return addNode(listItem, listKey);
+    }
+
+    /**
+     * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by
+     * the builder
+     *
+     * @param container
+     * @param <N>
+     * @return
+     */
+    @Override
+    public <N extends DataObject & Augmentation<? super T>> InstanceIdentifierBuilderImpl<N> augmentation(final Class<N> container) {
+        return addNode(container);
+    }
+
+    @Override
+    public InstanceIdentifier<T> build() {
+        Preconditions.checkState(arg != null, "No path arguments present");
+
+        final Iterable<PathArgument> pathArguments;
+        if (basePath == null) {
+            pathArguments = pathBuilder.build();
+        } else {
+            pathArguments = Iterables.concat(basePath, pathBuilder.build());
+        }
+
+        @SuppressWarnings("unchecked")
+        final InstanceIdentifier<T> ret = (InstanceIdentifier<T>) InstanceIdentifier.trustedCreate(arg, pathArguments, hashBuilder.toInstance(), wildcard);
+        return ret;
+    }
+
+    @Override
+    public InstanceIdentifier<T> toInstance() {
+        return build();
+    }
+}
diff --git a/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/KeyedInstanceIdentifier.java b/yang/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/KeyedInstanceIdentifier.java
new file mode 100644 (file)
index 0000000..8d9dcdc
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.binding;
+
+public class KeyedInstanceIdentifier<T extends Identifiable<K> & DataObject, K extends Identifier<T>> extends InstanceIdentifier<T> {
+    private final K key;
+
+    KeyedInstanceIdentifier(final Class<T> type, final Iterable<PathArgument> pathArguments, final boolean wildcarded, final int hash, final K key) {
+        super(type, pathArguments, wildcarded, hash);
+        this.key = key;
+    }
+
+    public final K getKey() {
+        return key;
+    }
+
+    @Override
+    public final InstanceIdentifierBuilder<T> builder() {
+        return new InstanceIdentifierBuilderImpl<T>(new InstanceIdentifier.IdentifiableItem<T, K>(getTargetType(), key), getPathArguments(), hashCode(), isWildcarded());
+    }
+}
index 792ccd0ccf2018ede2d1dac58043a92a376a98c3..e4188b470da3722e6004c114207d3954c80260aa 100644 (file)
@@ -33,7 +33,7 @@ public class InstanceIdentifierTest {
         assertEquals(Nodes.class, nodes.getTargetType());
 
 
-        InstanceIdentifier<Node> node = InstanceIdentifier.builder(nodes).child(Node.class).toInstance();
+        InstanceIdentifier<Node> node = nodes.builder().child(Node.class).toInstance();
 
         assertNotNull(node);
         assertEquals(Node.class, node.getTargetType());