*/
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;
+import java.io.IOException;
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+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 org.opendaylight.yangtools.util.HashCodeBuilder;
/**
*
* 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 Field PATHARGUMENTS_FIELD;
+ private static final long serialVersionUID = 2L;
/*
* 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;
+ protected transient final Iterable<PathArgument> pathArguments;
private final Class<T> targetType;
private final boolean wildcarded;
private final int hash;
+ static {
+ final Field f;
+ try {
+ f = InstanceIdentifier.class.getDeclaredField("pathArguments");
+ } catch (NoSuchFieldException | SecurityException e) {
+ throw new ExceptionInInitializerError(e);
+ }
+ f.setAccessible(true);
+ PATHARGUMENTS_FIELD = f;
+ }
+
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);
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;
}
}
@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")
}
/**
- * 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;
}
Preconditions.checkArgument(a != null, "pathArguments may not be empty");
- final Iterable<PathArgument> immutableArguments;
+ return trustedCreate(a, pathArguments, hashBuilder.build(), 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);
}
/**
*
* @param id instance identifier
* @return key associated with the last component
+ * @throws IllegalArgumentException if the supplied identifier type cannot have a key.
+ * @throws NullPointerException if id is null.
*/
public static <N extends Identifiable<K> & DataObject, K extends Identifier<N>> K keyOf(final InstanceIdentifier<N> id) {
+ Preconditions.checkNotNull(id);
+ Preconditions.checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
+
@SuppressWarnings("unchecked")
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())) {
+ if (Identifiable.class.isAssignableFrom(arg.getType()) && !(wildcarded)) {
Identifier<?> key = null;
if (arg instanceof IdentifiableItem<?, ?>) {
key = ((IdentifiableItem<?, ?>)arg).key;
* 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) {
@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
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());
}
}
* @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);
}
* @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) {
@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
* @return
*/
InstanceIdentifier<T> build();
+
+ /*
+ * @deprecated use #build()
+ */
+ @Deprecated
+ InstanceIdentifier<T> toInstance();
+ }
+
+ private void writeObject(final java.io.ObjectOutputStream out) throws IOException {
+ out.defaultWriteObject();
+ out.writeInt(Iterables.size(pathArguments));
+ for (Object o : pathArguments) {
+ out.writeObject(o);
+ }
+ }
+
+ private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
+ in.defaultReadObject();
+
+ final int size = in.readInt();
+ final List<PathArgument> args = new ArrayList<>(size);
+ for (int i = 0; i < size; ++i) {
+ args.add((PathArgument) in.readObject());
+ }
+
+ try {
+ PATHARGUMENTS_FIELD.set(this, ImmutableList.copyOf(args));
+ } catch (IllegalArgumentException | IllegalAccessException e) {
+ throw new IOException(e);
+ }
}
}