Merge "Bug 1372 - toString methods in generated classes"
[yangtools.git] / yang / yang-binding / src / main / java / org / opendaylight / yangtools / yang / binding / InstanceIdentifier.java
index 04f614da9385bd40709b75eadf2f648f4913503b..9a597152c3ce4f3aa1a4e04e8ff94c230378e0c4 100644 (file)
@@ -7,6 +7,15 @@
  */
 package org.opendaylight.yangtools.yang.binding;
 
+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;
+
+import java.io.IOException;
+import java.io.Serializable;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -14,13 +23,7 @@ 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;
+import org.opendaylight.yangtools.util.HashCodeBuilder;
 
 /**
  *
@@ -51,7 +54,8 @@ 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 class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>, Immutable {
+public class InstanceIdentifier<T extends DataObject> implements Path<InstanceIdentifier<? extends DataObject>>, Immutable, Serializable {
+    private static final long serialVersionUID = 1L;
     /*
      * Protected to differentiate internal and external access. Internal
      * access is required never to modify the contents. References passed
@@ -103,7 +107,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
     }
 
     @Override
-    public boolean equals(final Object obj) {
+    public final boolean equals(final Object obj) {
         if (this == obj) {
             return true;
         }
@@ -115,13 +119,45 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         }
 
         final InstanceIdentifier<?> other = (InstanceIdentifier<?>) obj;
+        if (pathArguments == other.pathArguments) {
+            return true;
+        }
+
+        /*
+         * We could now just go and compare the pathArguments, but that
+         * can be potentially expensive. Let's try to avoid that by
+         * checking various things that we have cached from pathArguments
+         * and trying to prove the identifiers are *not* equal.
+         */
         if (hash != other.hash) {
             return false;
         }
+        if (wildcarded != other.wildcarded) {
+            return false;
+        }
+        if (targetType != other.targetType) {
+            return false;
+        }
+        if (fastNonEqual(other)) {
+            return false;
+        }
 
+        // Everything checks out so far, so we have to do a full equals
         return Iterables.elementsEqual(pathArguments, other.pathArguments);
     }
 
+    /**
+     * Perform class-specific fast checks for non-equality. This allows
+     * subclasses to avoid iterating over the pathArguments by performing
+     * quick checks on their specific fields.
+     *
+     * @param other The other identifier, guaranteed to be the same class
+     * @return @true if the other identifier cannot be equal to this one.
+     */
+    protected boolean fastNonEqual(final InstanceIdentifier<?> other) {
+        return false;
+    }
+
     @Override
     public final String toString() {
         return addToStringAttributes(Objects.toStringHelper(this)).toString();
@@ -161,7 +197,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         for (final PathArgument a : getPathArguments()) {
             if (type.equals(a.getType())) {
                 @SuppressWarnings("unchecked")
-                final InstanceIdentifier<I> ret = (InstanceIdentifier<I>) create(Iterables.limit(getPathArguments(), i));
+                final InstanceIdentifier<I> ret = (InstanceIdentifier<I>) internalCreate(Iterables.limit(getPathArguments(), i));
                 return ret;
             }
 
@@ -285,10 +321,10 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
     }
 
     @SuppressWarnings("unchecked")
-    public final <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> InstanceIdentifier<N> child(
+    public final <N extends Identifiable<K> & ChildOf<? super T>, K extends Identifier<N>> KeyedInstanceIdentifier<N, K> child(
             final Class<N> listItem, final K listKey) {
         final PathArgument arg = new IdentifiableItem<>(listItem, listKey);
-        return (InstanceIdentifier<N>) childIdentifier(arg);
+        return (KeyedInstanceIdentifier<N, K>) childIdentifier(arg);
     }
 
     @SuppressWarnings("unchecked")
@@ -353,20 +389,17 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
     }
 
     /**
-     * Create an instance identifier for a very specific object type.
+     * Create an instance identifier for a very specific object type. This method
+     * implements {@link #create(Iterable)} semantics, except it is used by internal
+     * callers, which have assured that the argument is an immutable Iterable.
      *
-     * 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) {
+    private static InstanceIdentifier<?> internalCreate(final Iterable<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;
@@ -385,14 +418,31 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
         }
         Preconditions.checkArgument(a != null, "pathArguments may not be empty");
 
-        final Iterable<PathArgument> immutableArguments;
+        return trustedCreate(a, pathArguments, hashBuilder.toInstance(), wildcard);
+    }
+
+    /**
+     * 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) {
         if (pathArguments instanceof ImmutableCollection<?>) {
-            immutableArguments = (Iterable<PathArgument>) pathArguments;
+            @SuppressWarnings("unchecked")
+            final Iterable<PathArgument> immutableArguments = (Iterable<PathArgument>) pathArguments;
+            return internalCreate(immutableArguments);
         } else {
-            immutableArguments = ImmutableList.copyOf(pathArguments);
+            return internalCreate(ImmutableList.copyOf(pathArguments));
         }
-
-        return trustedCreate(a, immutableArguments, hashBuilder.toInstance(), wildcard);
     }
 
     /**
@@ -426,7 +476,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     static InstanceIdentifier<?> trustedCreate(final PathArgument arg, final Iterable<PathArgument> pathArguments, final int hash, boolean wildcarded) {
-        if (Identifiable.class.isAssignableFrom(arg.getType())) {
+        if (Identifiable.class.isAssignableFrom(arg.getType()) && !(wildcarded)) {
             Identifier<?> key = null;
             if (arg instanceof IdentifiableItem<?, ?>) {
                 key = ((IdentifiableItem<?, ?>)arg).key;
@@ -446,11 +496,12 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
      * Interface which implementations are used as path components of the
      * path in overall data tree.
      */
-    public interface PathArgument {
+    public interface PathArgument extends Comparable<PathArgument> {
         Class<? extends DataObject> getType();
     }
 
-    private static abstract class AbstractPathArgument<T extends DataObject> implements PathArgument {
+    private static abstract class AbstractPathArgument<T extends DataObject> implements PathArgument, Serializable {
+        private static final long serialVersionUID = 1L;
         private final Class<T> type;
 
         protected AbstractPathArgument(final Class<T> type) {
@@ -464,10 +515,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
 
         @Override
         public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + ((type == null) ? 0 : type.hashCode());
-            return result;
+            return type.hashCode();
         }
 
         @Override
@@ -482,14 +530,12 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
                 return false;
             }
             final AbstractPathArgument<?> other = (AbstractPathArgument<?>) obj;
-            if (type == null) {
-                if (other.type != null) {
-                    return false;
-                }
-            } else if (!type.equals(other.type)) {
-                return false;
-            }
-            return true;
+            return type.equals(other.type);
+        }
+
+        @Override
+        public int compareTo(final PathArgument arg) {
+            return type.getCanonicalName().compareTo(arg.getType().getCanonicalName());
         }
     }
 
@@ -500,6 +546,8 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
      * @param <T>
      */
     public static final class Item<T extends DataObject> extends AbstractPathArgument<T> {
+        private static final long serialVersionUID = 1L;
+
         public Item(final Class<T> type) {
             super(type);
         }
@@ -518,6 +566,7 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
      * @param <T> The identifier of the object
      */
     public static final class IdentifiableItem<I extends Identifiable<T> & DataObject, T extends Identifier<I>> extends AbstractPathArgument<I> {
+        private static final long serialVersionUID = 1L;
         private final T key;
 
         public IdentifiableItem(final Class<I> type, final T key) {
@@ -531,12 +580,12 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
 
         @Override
         public boolean equals(final Object obj) {
-            return super.equals(obj) && obj.hashCode() == hashCode() && key.equals(((IdentifiableItem<?, ?>) obj).getKey());
+            return super.equals(obj) && key.equals(((IdentifiableItem<?, ?>) obj).getKey());
         }
 
         @Override
         public int hashCode() {
-            return key.hashCode();
+            return super.hashCode() * 31 + key.hashCode();
         }
 
         @Override
@@ -602,4 +651,18 @@ public class InstanceIdentifier<T extends DataObject> implements Path<InstanceId
          */
         InstanceIdentifier<T> build();
     }
+
+    private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
+        out.writeObject(targetType);
+        out.writeBoolean(wildcarded);
+        out.writeInt(hash);
+        out.write(Iterables.size(pathArguments));
+        for (Object o : pathArguments) {
+            out.writeObject(o);
+        }
+    }
+
+    private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+        // TODO Auto-generated method stub
+    }
 }