X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fyang-binding%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fbinding%2FInstanceIdentifier.java;h=0c89142533d5c337fc8a503337f7b5b949fc8177;hb=eb7ab8e1bb6a28cfafd22a5a62ea66e5f85a8c2d;hp=240b5036e44531c43a99502cc4146e790b496994;hpb=2415b5ff41e02a13fc7811c27ca9766f58d00cec;p=mdsal.git diff --git a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java index 240b5036e4..0c89142533 100644 --- a/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java +++ b/binding/yang-binding/src/main/java/org/opendaylight/yangtools/yang/binding/InstanceIdentifier.java @@ -7,30 +7,35 @@ */ package org.opendaylight.yangtools.yang.binding; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.base.Verify.verify; +import static com.google.common.base.Verify.verifyNotNull; +import static java.util.Objects.requireNonNull; + import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; -import com.google.common.base.Preconditions; +import com.google.common.base.VerifyException; 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.ObjectStreamException; 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 java.util.Objects; +import java.util.Optional; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.concepts.HierarchicalIdentifier; import org.opendaylight.yangtools.util.HashCodeBuilder; /** - * * This instance identifier uniquely identifies a specific DataObject in the data tree modeled by YANG. * + *

* For Example let's say you were trying to refer to a node in inventory which was modeled in YANG as follows, * + *

*

  * module opendaylight-inventory {
  *      ....
@@ -47,40 +52,32 @@ import org.opendaylight.yangtools.util.HashCodeBuilder;
  * }
  * 
* - * You could create an instance identifier as follows to get to a node with id "openflow:1" - * + *

+ * You can create an instance identifier as follows to get to a node with id "openflow:1": {@code * InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build(); + * } * + *

* 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 implements Path>, Immutable, Serializable { - private static final Field PATHARGUMENTS_FIELD; - private static final long serialVersionUID = 2L; +public class InstanceIdentifier + implements HierarchicalIdentifier> { + private static final long serialVersionUID = 3L; + /* - * 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 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 transient final Iterable pathArguments; - private final Class targetType; + final Iterable pathArguments; + + private final @NonNull Class 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 type, final Iterable pathArguments, final boolean wildcarded, final int hash) { - this.pathArguments = Preconditions.checkNotNull(pathArguments); - this.targetType = Preconditions.checkNotNull(type); + InstanceIdentifier(final Class type, final Iterable pathArguments, final boolean wildcarded, + final int hash) { + this.pathArguments = requireNonNull(pathArguments); + this.targetType = requireNonNull(type); this.wildcarded = wildcarded; this.hash = hash; } @@ -90,22 +87,35 @@ public class InstanceIdentifier implements Path getTargetType() { + public final @NonNull Class getTargetType() { return targetType; } + /** + * Perform a safe target type adaptation of this instance identifier to target type. This method is useful when + * dealing with type-squashed instances. + * + * @return Path argument with target type + * @throws VerifyException if this instance identifier cannot be adapted to target type + * @throws NullPointerException if {@code target} is null + */ + @SuppressWarnings("unchecked") + public final @NonNull InstanceIdentifier verifyTarget(final Class<@NonNull N> target) { + verify(target.equals(targetType), "Cannot adapt %s to %s", this, target); + return (InstanceIdentifier) this; + } + /** * Return the path argument chain which makes up this instance identifier. * * @return Path argument chain. Immutable and does not contain nulls. */ - public final Iterable getPathArguments() { + public final @NonNull Iterable getPathArguments() { return Iterables.unmodifiableIterable(pathArguments); } /** - * Check whether an instance identifier contains any wildcards. A wildcard - * is an path argument which has a null key. + * Check whether an instance identifier contains any wildcards. A wildcard is an path argument which has a null key. * * @return true if any of the path arguments has a null key. */ @@ -159,9 +169,8 @@ public class InstanceIdentifier implements Path implements Path * For example let's say an instance identifier was built like so, *

-     *      identifier = InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class, new NodeKey(new NodeId("openflow:1")).build();
+     *      identifier = InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class,
+     *                   new NodeKey(new NodeId("openflow:1")).build();
      * 
* + *

* And you wanted to obtain the Instance identifier which represented Nodes you would do it like so, * + *

*

      *      identifier.firstIdentifierOf(Nodes.class)
      * 
@@ -204,16 +216,18 @@ public class InstanceIdentifier implements Path InstanceIdentifier firstIdentifierOf(final Class type) { - int i = 1; + public final @Nullable InstanceIdentifier firstIdentifierOf( + final Class<@NonNull I> type) { + int count = 1; for (final PathArgument a : pathArguments) { if (type.equals(a.getType())) { @SuppressWarnings("unchecked") - final InstanceIdentifier ret = (InstanceIdentifier) internalCreate(Iterables.limit(pathArguments, i)); + final InstanceIdentifier ret = (InstanceIdentifier) internalCreate( + Iterables.limit(pathArguments, count)); return ret; } - ++i; + ++count; } return null; @@ -224,26 +238,11 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier> K firstKeyOf(final Class listItem, final Class listKey) { - return firstKeyOf(listItem); - } - - /** - * Return the key associated with the first component of specified type in - * an identifier. - * - * @param listItem component type - * @return key associated with the component, or null if the component type - * is not present. - */ - public final & DataObject, K extends Identifier> K firstKeyOf(final Class listItem) { + public final & DataObject, K extends Identifier> @Nullable K firstKeyOf( + final Class<@NonNull N> listItem) { for (final PathArgument i : pathArguments) { if (listItem.equals(i.getType())) { @SuppressWarnings("unchecked") @@ -257,27 +256,27 @@ public class InstanceIdentifier implements Path * 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, - * + *

+ * To illustrate here is an example which explains the working of this API. Let's say you have two instance + * identifiers as follows: + * {@code * this = /nodes/node/openflow:1 * other = /nodes/node/openflow:2 - * + * } * then this.contains(other) will return false. * - * @param other - * @return + * @param other Potentially-container instance identifier + * @return True if the specified identifier is contained in this identifier. */ @Override public final boolean contains(final InstanceIdentifier other) { - Preconditions.checkNotNull(other, "other should not be null"); + requireNonNull(other, "other should not be null"); final Iterator lit = pathArguments.iterator(); final Iterator oit = other.pathArguments.iterator(); @@ -304,7 +303,7 @@ public class InstanceIdentifier implements Path other) { - Preconditions.checkNotNull(other, "other should not be null"); + requireNonNull(other, "other should not be null"); final Iterator lit = pathArguments.iterator(); final Iterator oit = other.pathArguments.iterator(); @@ -328,130 +327,239 @@ public class InstanceIdentifier implements Path @NonNull InstanceIdentifier childIdentifier(final AbstractPathArgument arg) { + return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)), + HashCodeBuilder.nextHashCode(hash, arg), isWildcarded()); + } + /** - * Create a builder rooted at this key. + * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to + * {@code builder().child(container).build()}. * - * @return A builder instance + * @param container Container to append + * @param Container type + * @return An InstanceIdentifier. + * @throws NullPointerException if {@code container} is null */ - public InstanceIdentifierBuilder builder() { - return new InstanceIdentifierBuilderImpl(new Item(targetType), pathArguments, hash, isWildcarded()); - } - - private InstanceIdentifier childIdentifier(final PathArgument arg) { - return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)), HashCodeBuilder.nextHashCode(hash, arg), isWildcarded()); + public final > @NonNull InstanceIdentifier child( + final Class<@NonNull N> container) { + return childIdentifier(Item.of(container)); } + /** + * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to + * {@code builder().child(listItem, listKey).build()}. + * + * @param listItem List to append + * @param listKey List key + * @param List type + * @param Key type + * @return An InstanceIdentifier. + * @throws NullPointerException if any argument is null + */ @SuppressWarnings("unchecked") - public final > InstanceIdentifier child(final Class container) { - final PathArgument arg = new Item<>(container); - return (InstanceIdentifier) childIdentifier(arg); + public final & ChildOf, K extends Identifier> + @NonNull KeyedInstanceIdentifier child(final Class<@NonNull N> listItem, final K listKey) { + return (KeyedInstanceIdentifier) childIdentifier(IdentifiableItem.of(listItem, listKey)); } - @SuppressWarnings("unchecked") - public final & ChildOf, K extends Identifier> KeyedInstanceIdentifier child( - final Class listItem, final K listKey) { - final PathArgument arg = new IdentifiableItem<>(listItem, listKey); - return (KeyedInstanceIdentifier) childIdentifier(arg); + /** + * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to + * {@code builder().child(caze, container).build()}. + * + * @param caze Choice case class + * @param container Container to append + * @param Case type + * @param Container type + * @return An InstanceIdentifier. + * @throws NullPointerException if any argument is null + */ + // FIXME: add a proper caller + public final & DataObject, N extends ChildOf> + @NonNull InstanceIdentifier child(final Class<@NonNull C> caze, final Class<@NonNull N> container) { + return childIdentifier(Item.of(caze, container)); } + /** + * Create an InstanceIdentifier for a child list item. This method is a more efficient equivalent to + * {@code builder().child(caze, listItem, listKey).build()}. + * + * @param caze Choice case class + * @param listItem List to append + * @param listKey List key + * @param Case type + * @param List type + * @param Key type + * @return An InstanceIdentifier. + * @throws NullPointerException if any argument is null + */ + // FIXME: add a proper caller @SuppressWarnings("unchecked") - public final > InstanceIdentifier augmentation( - final Class container) { - final PathArgument arg = new Item<>(container); - return (InstanceIdentifier) childIdentifier(arg); + public final & DataObject, K extends Identifier, + N extends Identifiable & ChildOf> @NonNull KeyedInstanceIdentifier child( + final Class<@NonNull C> caze, final Class<@NonNull N> listItem, final K listKey) { + return (KeyedInstanceIdentifier) childIdentifier(IdentifiableItem.of(caze, listItem, listKey)); } - @Deprecated - private List legacyCache; - /** - * @deprecated Use {@link #getPathArguments()} instead. + * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to + * {@code builder().augmentation(container).build()}. + * + * @param container Container to append + * @param Container type + * @return An InstanceIdentifier. + * @throws NullPointerException if {@code container} is null */ - @Deprecated - public final List getPath() { - if (legacyCache == null) { - legacyCache = ImmutableList.copyOf(pathArguments); - } - - return legacyCache; + public final > @NonNull InstanceIdentifier augmentation( + final Class<@NonNull N> container) { + return childIdentifier(Item.of(container)); } /** - * Create a new InstanceIdentifierBuilder given a base InstanceIdentifier - * - * @param base - * @param - * @return + * Create a builder rooted at this key. * - * @deprecated Use {@link #builder()} instead. + * @return A builder instance */ - @Deprecated - public static InstanceIdentifierBuilder builder(final InstanceIdentifier base) { - return base.builder(); + // FIXME: rename this method to 'toBuilder()' + public @NonNull InstanceIdentifierBuilder builder() { + return new InstanceIdentifierBuilderImpl<>(Item.of(targetType), pathArguments, hash, isWildcarded()); } /** - * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container + * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container. * - * @param container - * @param - * @return + * @param container Base container + * @param Type of the container + * @return A new {@link InstanceIdentifierBuilder} + * @throws NullPointerException if {@code container} is null */ - public static > InstanceIdentifierBuilder builder(final Class container) { - return new InstanceIdentifierBuilderImpl().addNode(container); + public static > @NonNull InstanceIdentifierBuilder builder( + final Class container) { + return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(container)); } /** - * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an IdentifiableItem - * - * @param listItem - * @param listKey - * @param - * @param - * @return + * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container in + * a {@code grouping} used in the {@code case} statement. + * + * @param caze Choice case class + * @param container Base container + * @param Case type + * @param Type of the container + * @return A new {@link InstanceIdentifierBuilder} + * @throws NullPointerException if any argument is null */ - public static & ChildOf, K extends Identifier> InstanceIdentifierBuilder builder( - final Class listItem, final K listKey) { - return new InstanceIdentifierBuilderImpl().addNode(listItem, listKey); + public static & DataObject, T extends ChildOf> + @NonNull InstanceIdentifierBuilder builder(final Class caze, final Class container) { + return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(caze, container)); } /** - * 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. - * + * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an + * {@link IdentifiableItem}. + * + * @param listItem list item class + * @param listKey key value + * @param List type + * @param List key + * @return A new {@link InstanceIdentifierBuilder} + * @throws NullPointerException if any argument is null + */ + public static & ChildOf, + K extends Identifier> @NonNull InstanceIdentifierBuilder builder(final Class listItem, + final K listKey) { + return new InstanceIdentifierBuilderImpl().addNode(IdentifiableItem.of(listItem, listKey)); + } + + /** + * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an + * {@link IdentifiableItem} in a {@code grouping} used in the {@code case} statement. + * + * @param caze Choice case class + * @param listItem list item class + * @param listKey key value + * @param Case type + * @param List type + * @param List key + * @return A new {@link InstanceIdentifierBuilder} + * @throws NullPointerException if any argument is null + */ + public static & DataObject, + N extends Identifiable & ChildOf, K extends Identifier> + @NonNull InstanceIdentifierBuilder builder(final Class caze, final Class listItem, + final K listKey) { + return new InstanceIdentifierBuilderImpl().addNode(IdentifiableItem.of(caze, listItem, listKey)); + } + + public static > + @NonNull InstanceIdentifierBuilder builderOfInherited(final Class root, final Class container) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(container)); + } + + public static & DataObject, + T extends ChildOf> + @NonNull InstanceIdentifierBuilder builderOfInherited(final Class root, + final Class caze, final Class container) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(caze, container)); + } + + public static & ChildOf, + K extends Identifier> + @NonNull InstanceIdentifierBuilder builderOfInherited(final Class root, + final Class listItem, final K listKey) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new InstanceIdentifierBuilderImpl().addNode(IdentifiableItem.of(listItem, listKey)); + } + + public static & DataObject, + N extends Identifiable & ChildOf, K extends Identifier> + @NonNull InstanceIdentifierBuilder builderOfInherited(final Class root, + final Class caze, final Class listItem, final K listKey) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new InstanceIdentifierBuilderImpl().addNode(IdentifiableItem.of(caze, listItem, listKey)); + } + + /** + * 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. * * @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. + * @throws IllegalArgumentException if pathArguments is empty or contains a null element. + * @throws NullPointerException if {@code pathArguments} is null */ - private static InstanceIdentifier internalCreate(final Iterable pathArguments) { - final Iterator it = Preconditions.checkNotNull(pathArguments, "pathArguments may not be null").iterator(); + private static @NonNull InstanceIdentifier internalCreate(final Iterable pathArguments) { + final var it = requireNonNull(pathArguments, "pathArguments may not be null").iterator(); + checkArgument(it.hasNext(), "pathArguments may not be empty"); + final HashCodeBuilder hashBuilder = new HashCodeBuilder<>(); boolean wildcard = false; - PathArgument a = null; + PathArgument arg; - while (it.hasNext()) { - a = it.next(); - Preconditions.checkArgument(a != null, "pathArguments may not contain null elements"); + do { + arg = it.next(); + // Non-null is implied by our callers + final var type = verifyNotNull(arg).getType(); + checkArgument(ChildOf.class.isAssignableFrom(type) || Augmentation.class.isAssignableFrom(type), + "%s is not a valid path argument", type); - // TODO: sanity check ChildOf<>; - hashBuilder.addArgument(a); + hashBuilder.addArgument(arg); - if (Identifiable.class.isAssignableFrom(a.getType()) && !(a instanceof IdentifiableItem)) { + if (Identifiable.class.isAssignableFrom(type) && !(arg instanceof IdentifiableItem)) { wildcard = true; } - } - Preconditions.checkArgument(a != null, "pathArguments may not be empty"); + } while (it.hasNext()); - return trustedCreate(a, pathArguments, hashBuilder.build(), wildcard); + return trustedCreate(arg, pathArguments, hashBuilder.build(), wildcard); } /** * Create an instance identifier for a very specific object type. * - * Example + *

+ * Example: *

      *  List<PathArgument> path = Arrays.asList(new Item(Nodes.class))
      *  new InstanceIdentifier(path);
@@ -462,19 +570,21 @@ public class InstanceIdentifier implements Path create(final Iterable pathArguments) {
-        if (pathArguments instanceof ImmutableCollection) {
+    // FIXME: rename to 'unsafeOf()'
+    public static @NonNull InstanceIdentifier create(final Iterable pathArguments) {
+        if (pathArguments instanceof ImmutableCollection) {
             @SuppressWarnings("unchecked")
-            final Iterable immutableArguments = (Iterable) pathArguments;
+            final var immutableArguments = (ImmutableCollection) pathArguments;
             return internalCreate(immutableArguments);
-        } else {
-            return internalCreate(ImmutableList.copyOf(pathArguments));
         }
+
+        return internalCreate(ImmutableList.copyOf(pathArguments));
     }
 
     /**
      * Create an instance identifier for a very specific object type.
      *
+     * 

* For example *

      *      new InstanceIdentifier(Nodes.class)
@@ -484,9 +594,11 @@ public class InstanceIdentifier implements Path InstanceIdentifier create(final Class type) {
-        return (InstanceIdentifier) create(Collections. singletonList(new Item<>(type)));
+    public static > @NonNull InstanceIdentifier create(
+            final Class<@NonNull T> type) {
+        return (InstanceIdentifier) internalCreate(ImmutableList.of(Item.of(type)));
     }
 
     /**
@@ -497,9 +609,11 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier> K keyOf(final InstanceIdentifier id) {
-        Preconditions.checkNotNull(id);
-        Preconditions.checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
+    // FIXME: reconsider naming and design of this method
+    public static  & DataObject, K extends Identifier> K keyOf(
+            final InstanceIdentifier id) {
+        requireNonNull(id);
+        checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id);
 
         @SuppressWarnings("unchecked")
         final K ret = ((KeyedInstanceIdentifier)id).getKey();
@@ -507,37 +621,53 @@ public class InstanceIdentifier implements Path trustedCreate(final PathArgument arg, final Iterable pathArguments, final int hash, boolean wildcarded) {
-        if (Identifiable.class.isAssignableFrom(arg.getType()) && !(wildcarded)) {
+    static  @NonNull InstanceIdentifier trustedCreate(final PathArgument arg,
+            final Iterable pathArguments, final int hash, boolean wildcarded) {
+        if (Identifiable.class.isAssignableFrom(arg.getType()) && !wildcarded) {
             Identifier key = null;
-            if (arg instanceof IdentifiableItem) {
-                key = ((IdentifiableItem)arg).key;
+            if (arg instanceof IdentifiableItem) {
+                key = ((IdentifiableItem)arg).getKey();
             } else {
                 wildcarded = true;
             }
 
             return new KeyedInstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash, key);
-        } else {
-            return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash);
         }
+
+        return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash);
     }
 
     /**
-     * Path argument of {@link InstanceIdentifier}.
-     * 

- * Interface which implementations are used as path components of the + * Path argument of {@link InstanceIdentifier}. Interface which implementations are used as path components of the * path in overall data tree. */ public interface PathArgument extends Comparable { - Class getType(); + /** + * Return the data object type backing this PathArgument. + * + * @return Data object type. + */ + @NonNull Class getType(); + + /** + * Return an optional enclosing case type. This is used only when {@link #getType()} references a node defined + * in a {@code grouping} which is reference inside a {@code case} statement in order to safely reference the + * node. + * + * @return Optional case class. + */ + default Optional> getCaseType() { + return Optional.empty(); + } } - private static abstract class AbstractPathArgument implements PathArgument, Serializable { + private abstract static class AbstractPathArgument implements PathArgument, Serializable { private static final long serialVersionUID = 1L; - private final Class type; - protected AbstractPathArgument(final Class type) { - this.type = Preconditions.checkNotNull(type, "Type may not be null."); + private final @NonNull Class type; + + AbstractPathArgument(final Class type) { + this.type = requireNonNull(type, "Type may not be null."); } @Override @@ -545,29 +675,44 @@ public class InstanceIdentifier implements Path other = (AbstractPathArgument) obj; - return type.equals(other.type); + return type.equals(other.type) && Objects.equals(getKey(), other.getKey()) + && getCaseType().equals(other.getCaseType()); } @Override - public int compareTo(final PathArgument arg) { - return type.getCanonicalName().compareTo(arg.getType().getCanonicalName()); + public final int compareTo(final PathArgument arg) { + final int cmp = compareClasses(type, arg.getType()); + if (cmp != 0) { + return cmp; + } + final Optional> caseType = getCaseType(); + if (!caseType.isPresent()) { + return arg.getCaseType().isPresent() ? -1 : 1; + } + final Optional> argCaseType = getCaseType(); + return argCaseType.isPresent() ? compareClasses(caseType.get(), argCaseType.get()) : 1; + } + + private static int compareClasses(final Class first, final Class second) { + return first.getCanonicalName().compareTo(second.getCanonicalName()); } } @@ -575,15 +720,43 @@ public class InstanceIdentifier implements Path + * @param Item type */ - public static final class Item extends AbstractPathArgument { + public static class Item extends AbstractPathArgument { private static final long serialVersionUID = 1L; - public Item(final Class type) { + Item(final Class type) { super(type); } + /** + * Return a PathArgument instance backed by the specified class. + * + * @param type Backing class + * @param Item type + * @return A new PathArgument + * @throws NullPointerException if {@code} is null. + */ + public static @NonNull Item of(final Class type) { + return new Item<>(type); + } + + /** + * Return a PathArgument instance backed by the specified class, which in turn is defined in a {@code grouping} + * used in a corresponding {@code case} statement. + * + * @param caseType defining case class + * @param type Backing class + * @param Case type + * @param Item type + * @return A new PathArgument + * @throws NullPointerException if any argument is null. + */ + public static & DataObject, T extends ChildOf> @NonNull Item of( + final Class caseType, final Class type) { + return new CaseItem<>(caseType, type); + } + @Override public String toString() { return getType().getName(); @@ -597,27 +770,58 @@ public class InstanceIdentifier implements Path An object that is identifiable by an identifier * @param The identifier of the object */ - public static final class IdentifiableItem & DataObject, T extends Identifier> extends AbstractPathArgument { + public static class IdentifiableItem & DataObject, T extends Identifier> + extends AbstractPathArgument { private static final long serialVersionUID = 1L; - private final T key; - public IdentifiableItem(final Class type, final T key) { + private final @NonNull T key; + + IdentifiableItem(final Class type, final T key) { super(type); - this.key = Preconditions.checkNotNull(key, "Key may not be null."); + this.key = requireNonNull(key, "Key may not be null."); } - public T getKey() { - return this.key; + /** + * Return an IdentifiableItem instance backed by the specified class with specified key. + * + * @param type Backing class + * @param key Key + * @param List type + * @param Key type + * @return An IdentifiableItem + * @throws NullPointerException if any argument is null. + */ + public static & DataObject, I extends Identifier> + @NonNull IdentifiableItem of(final Class type, final I key) { + return new IdentifiableItem<>(type, key); } - @Override - public boolean equals(final Object obj) { - return super.equals(obj) && key.equals(((IdentifiableItem) obj).getKey()); + /** + * Return an IdentifiableItem instance backed by the specified class with specified key. The class is in turn + * defined in a {@code grouping} used in a corresponding {@code case} statement. + * + * @param caseType defining case class + * @param type Backing class + * @param Case type + * @param List type + * @param Key type + * @return A new PathArgument + * @throws NullPointerException if any argument is null. + */ + public static & DataObject, T extends ChildOf & Identifiable, + I extends Identifier> @NonNull IdentifiableItem of(final Class caseType, + final Class type, final I key) { + return new CaseIdentifiableItem<>(caseType, type, key); } + /** + * Return the data object type backing this PathArgument. + * + * @return Data object type. + */ @Override - public int hashCode() { - return super.hashCode() * 31 + key.hashCode(); + public final @NonNull T getKey() { + return key; } @Override @@ -626,92 +830,131 @@ public class InstanceIdentifier implements Path & DataObject, T extends ChildOf> + extends Item { + private static final long serialVersionUID = 1L; + + private final Class caseType; + + CaseItem(final Class caseType, final Class type) { + super(type); + this.caseType = requireNonNull(caseType); + } + + @Override + public Optional> getCaseType() { + return Optional.of(caseType); + } + } + + private static final class CaseIdentifiableItem & DataObject, + T extends ChildOf & Identifiable, K extends Identifier> extends IdentifiableItem { + private static final long serialVersionUID = 1L; + + private final Class caseType; + + CaseIdentifiableItem(final Class caseType, final Class type, final K key) { + super(type, key); + this.caseType = requireNonNull(caseType); + } + + @Override + public Optional> getCaseType() { + return Optional.of(caseType); + } + } - public interface InstanceIdentifierBuilder extends Builder> { + // FIXME: rename to 'Builder' + // FIXME: introduce KeyedBuilder with specialized build() method + public interface InstanceIdentifierBuilder { /** - * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. - * - * This method should be used when you want to build an instance identifier by appending top-level - * elements - * - * Example, + * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This + * method should be used when you want to build an instance identifier by appending top-level elements, for + * example *

          *     InstanceIdentifier.builder().child(Nodes.class).build();
-         *
          * 
* + *

* NOTE :- The above example is only for illustration purposes InstanceIdentifier.builder() has been deprecated * and should not be used. Use InstanceIdentifier.builder(Nodes.class) instead * - * @param container - * @param - * @return + * @param container Container to append + * @param Container type + * @return this builder + * @throws NullPointerException if {@code container} is null */ - > InstanceIdentifierBuilder child( - Class container); + > @NonNull InstanceIdentifierBuilder child(Class container); + + /** + * Append the specified container as a child of the current InstanceIdentifier referenced by the builder. This + * method should be used when you want to build an instance identifier by appending a container node to the + * identifier and the {@code container} is defined in a {@code grouping} used in a {@code case} statement. + * + * @param caze Choice case class + * @param container Container to append + * @param Case type + * @param Container type + * @return this builder + * @throws NullPointerException if {@code container} is null + */ + & DataObject, N extends ChildOf> + @NonNull InstanceIdentifierBuilder child(Class caze, Class container); /** - * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. + * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This + * method should be used when you want to build an instance identifier by appending a specific list element to + * the identifier. * - * This method should be used when you want to build an instance identifier by appending a specific list element - * to the identifier + * @param listItem List to append + * @param listKey List key + * @param List type + * @param Key type + * @return this builder + * @throws NullPointerException if any argument is null + */ + & ChildOf, K extends Identifier> + @NonNull InstanceIdentifierBuilder child(Class<@NonNull N> listItem, K listKey); + + /** + * Append the specified listItem as a child of the current InstanceIdentifier referenced by the builder. This + * method should be used when you want to build an instance identifier by appending a specific list element to + * the identifier and the {@code list} is defined in a {@code grouping} used in a {@code case} statement. * - * @param listItem - * @param listKey - * @param - * @param - * @return + * @param caze Choice case class + * @param listItem List to append + * @param listKey List key + * @param Case type + * @param List type + * @param Key type + * @return this builder + * @throws NullPointerException if any argument is null */ - & ChildOf, K extends Identifier> InstanceIdentifierBuilder child( - Class listItem, K listKey); + & DataObject, K extends Identifier, + N extends Identifiable & ChildOf> @NonNull InstanceIdentifierBuilder child( + Class caze, Class listItem, K listKey); /** * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by - * the builder + * the builder. * - * @param container - * @param - * @return + * @param container augmentation class + * @param augmentation type + * @return this builder + * @throws NullPointerException if {@code container} is null */ - > InstanceIdentifierBuilder augmentation( + > @NonNull InstanceIdentifierBuilder augmentation( Class container); /** * Build the instance identifier. * - * @return - */ - @Override - InstanceIdentifier build(); - - /* - * @deprecated use #build() + * @return Resulting instance identifier. */ - @Deprecated - InstanceIdentifier 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); - } + @NonNull InstanceIdentifier build(); } - private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); - - final int size = in.readInt(); - final List 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); - } + private Object writeReplace() throws ObjectStreamException { + return new InstanceIdentifierV3<>(this); } }