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=22681a4d0f13e789f541c444d117e22d76b08df2;hb=106cf943e6837e6c30d7628b7c597ee57f0bfbd3;hp=6b71a31122aee22cedbb238b8e72c5c5882bb047;hpb=cda86b1c7a31b05eba8d2a0ad365fd68dd2ec1e3;p=yangtools.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 6b71a31122..22681a4d0f 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 @@ -8,25 +8,28 @@ 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.annotations.Beta; import com.google.common.base.MoreObjects; import com.google.common.base.MoreObjects.ToStringHelper; -import com.google.common.collect.ImmutableCollection; +import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +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 java.util.Objects; -import java.util.Optional; -import org.opendaylight.yangtools.concepts.Builder; -import org.opendaylight.yangtools.concepts.Immutable; -import org.opendaylight.yangtools.concepts.Path; +import org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.concepts.HierarchicalIdentifier; import org.opendaylight.yangtools.util.HashCodeBuilder; /** @@ -34,61 +37,49 @@ import org.opendaylight.yangtools.util.HashCodeBuilder; * *

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

code{
+ *   module opendaylight-inventory {
+ *     ....
  *
- * 

- *

- * module opendaylight-inventory {
- *      ....
- *
- *      container nodes {
- *        list node {
- *            key "id";
- *            ext:context-instance "node-context";
+ *     container nodes {
+ *       list node {
+ *         key "id";
+ *         ext:context-instance "node-context";
  *
- *            uses node;
- *        }
- *    }
- *
- * }
- * 
+ * uses node; + * } + * } + * } + * }
* *

* 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(); + * InstanceIdentifier.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 sealed class InstanceIdentifier + implements HierarchicalIdentifier> + permits KeyedInstanceIdentifier { + @java.io.Serial + 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 final transient 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, + InstanceIdentifier(final Class type, final Iterable> pathArguments, final boolean wildcarded, final int hash) { this.pathArguments = requireNonNull(pathArguments); - this.targetType = requireNonNull(type); + targetType = requireNonNull(type); this.wildcarded = wildcarded; this.hash = hash; } @@ -98,16 +89,30 @@ 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); } @@ -137,43 +142,24 @@ public class InstanceIdentifier implements Path other = (InstanceIdentifier) obj; + final var 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. + * 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); + return hash == other.hash && wildcarded == other.wildcarded && targetType == other.targetType + && keyEquals(other) + // Everything checks out so far, so we have to do a full equals + && 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; + boolean keyEquals(final InstanceIdentifier other) { + return true; } @Override @@ -197,7 +183,7 @@ public class InstanceIdentifier implements Path * For example let's say an instance identifier was built like so, *

-     *      identifier = InstanceIdentifierBuilder.builder(Nodes.class).child(Node.class,
+     *      identifier = InstanceIdentifier.builder(Nodes.class).child(Node.class,
      *                   new NodeKey(new NodeId("openflow:1")).build();
      * 
* @@ -213,13 +199,13 @@ public class InstanceIdentifier implements Path InstanceIdentifier firstIdentifierOf(final Class type) { + public final @Nullable InstanceIdentifier firstIdentifierOf( + final Class<@NonNull I> type) { int count = 1; - for (final PathArgument a : pathArguments) { - if (type.equals(a.getType())) { + for (var step : pathArguments) { + if (type.equals(step.type())) { @SuppressWarnings("unchecked") - final InstanceIdentifier ret = (InstanceIdentifier) internalCreate( - Iterables.limit(pathArguments, count)); + final var ret = (InstanceIdentifier) internalCreate(Iterables.limit(pathArguments, count)); return ret; } @@ -229,23 +215,6 @@ 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. @@ -254,16 +223,15 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier> K firstKeyOf( - final Class listItem) { - for (final PathArgument i : pathArguments) { - if (listItem.equals(i.getType())) { + public final & DataObject, K extends Key> @Nullable K firstKeyOf( + final Class<@NonNull N> listItem) { + for (var step : pathArguments) { + if (step instanceof KeyStep keyPredicate && listItem.equals(step.type())) { @SuppressWarnings("unchecked") - final K ret = ((IdentifiableItem)i).getKey(); + final var ret = (K) keyPredicate.key(); return ret; } } - return null; } @@ -291,19 +259,15 @@ public class InstanceIdentifier implements Path other) { requireNonNull(other, "other should not be null"); - final Iterator lit = pathArguments.iterator(); - final Iterator oit = other.pathArguments.iterator(); - - while (lit.hasNext()) { + final var oit = other.pathArguments.iterator(); + for (var step : pathArguments) { if (!oit.hasNext()) { return false; } - - if (!lit.next().equals(oit.next())) { + if (!step.equals(oit.next())) { return false; } } - return true; } @@ -318,31 +282,32 @@ public class InstanceIdentifier implements Path other) { requireNonNull(other, "other should not be null"); - final Iterator lit = pathArguments.iterator(); - final Iterator oit = other.pathArguments.iterator(); - - while (lit.hasNext()) { - if (!oit.hasNext()) { + final var otherSteps = other.pathArguments.iterator(); + for (var step : pathArguments) { + if (!otherSteps.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; + final var otherStep = otherSteps.next(); + if (step instanceof ExactDataObjectStep) { + if (!step.equals(otherStep)) { + return false; + } + } else if (step instanceof KeylessStep keyless) { + if (!keyless.matches(otherStep)) { + return false; + } + } else { + throw new IllegalStateException("Unhandled step " + step); } } return true; } - private InstanceIdentifier childIdentifier(final AbstractPathArgument arg) { + private @NonNull InstanceIdentifier childIdentifier(final DataObjectStep arg) { return trustedCreate(arg, Iterables.concat(pathArguments, Collections.singleton(arg)), - HashCodeBuilder.nextHashCode(hash, arg), isWildcarded()); + HashCodeBuilder.nextHashCode(hash, arg), wildcarded); } /** @@ -354,8 +319,9 @@ public class InstanceIdentifier implements Path> InstanceIdentifier child(final Class container) { - return childIdentifier(Item.of(container)); + public final > @NonNull InstanceIdentifier child( + final Class<@NonNull N> container) { + return childIdentifier(createStep(container)); } /** @@ -370,9 +336,9 @@ public class InstanceIdentifier implements Path & ChildOf, K extends Identifier> KeyedInstanceIdentifier - child(final Class listItem, final K listKey) { - return (KeyedInstanceIdentifier) childIdentifier(IdentifiableItem.of(listItem, listKey)); + public final & ChildOf, K extends Key> + @NonNull KeyedInstanceIdentifier child(final Class<@NonNull N> listItem, final K listKey) { + return (KeyedInstanceIdentifier) childIdentifier(new KeyStep<>(listItem, listKey)); } /** @@ -386,9 +352,10 @@ public class InstanceIdentifier implements Path & DataObject, N extends ChildOf> InstanceIdentifier child( - final Class caze, final Class container) { - return childIdentifier(Item.of(caze, container)); + // 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(createStep(caze, container)); } /** @@ -404,11 +371,12 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier, - N extends Identifiable & ChildOf> KeyedInstanceIdentifier child(final Class caze, - final Class listItem, final K listKey) { - return (KeyedInstanceIdentifier) childIdentifier(IdentifiableItem.of(caze, listItem, listKey)); + public final & DataObject, K extends Key, + N extends KeyAware & ChildOf> @NonNull KeyedInstanceIdentifier child( + final Class<@NonNull C> caze, final Class<@NonNull N> listItem, final K listKey) { + return (KeyedInstanceIdentifier) childIdentifier(new KeyStep<>(listItem, requireNonNull(caze), listKey)); } /** @@ -420,26 +388,33 @@ public class InstanceIdentifier implements Path> InstanceIdentifier augmentation( - final Class container) { - return childIdentifier(Item.of(container)); + public final > @NonNull InstanceIdentifier augmentation( + final Class<@NonNull N> container) { + return childIdentifier(new NodeStep<>(container)); } - @Deprecated - private List legacyCache; + @java.io.Serial + Object writeReplace() throws ObjectStreamException { + return new IIv4<>(this); + } - /** - * Return the path as a list. - * - * @deprecated Use {@link #getPathArguments()} instead. - */ - @Deprecated - public final List getPath() { - if (legacyCache == null) { - legacyCache = ImmutableList.copyOf(pathArguments); - } + @java.io.Serial + private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { + throwNSE(); + } + + @java.io.Serial + private void readObjectNoData() throws ObjectStreamException { + throwNSE(); + } - return legacyCache; + @java.io.Serial + private void writeObject(final ObjectOutputStream stream) throws IOException { + throwNSE(); + } + + private void throwNSE() throws NotSerializableException { + throw new NotSerializableException(getClass().getName()); } /** @@ -447,58 +422,59 @@ public class InstanceIdentifier implements Path builder() { - return new InstanceIdentifierBuilderImpl<>(Item.of(targetType), pathArguments, hash, isWildcarded()); + // FIXME: rename this method to 'toBuilder()' + public @NonNull Builder builder() { + return new RegularBuilder<>(this); } /** - * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container. + * Create a {@link Builder} for a specific type of InstanceIdentifier as specified by container. * * @param container Base container * @param Type of the container - * @return A new {@link InstanceIdentifierBuilder} + * @return A new {@link Builder} * @throws NullPointerException if {@code container} is null */ - public static > InstanceIdentifierBuilder builder( + public static > @NonNull Builder builder( final Class container) { - return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(container)); + return new RegularBuilder<>(createStep(container)); } /** - * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier as specified by container in + * Create a {@link Builder} 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} + * @return A new {@link Builder} * @throws NullPointerException if any argument is null */ public static & DataObject, T extends ChildOf> - InstanceIdentifierBuilder builder(final Class caze, final Class container) { - return new InstanceIdentifierBuilderImpl().addWildNode(Item.of(caze, container)); + @NonNull Builder builder(final Class caze, final Class container) { + return new RegularBuilder<>(createStep(caze, container)); } /** - * Create an InstanceIdentifierBuilder for a specific type of InstanceIdentifier which represents an - * {@link IdentifiableItem}. + * Create a {@link Builder} 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} + * @return A new {@link Builder} * @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(IdentifiableItem.of(listItem, listKey)); + public static & ChildOf, + K extends Key> @NonNull KeyedBuilder builder(final Class listItem, + final K listKey) { + return new KeyedBuilder<>(new KeyStep<>(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. + * Create a {@link Builder} 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 @@ -506,13 +482,56 @@ public class InstanceIdentifier implements Path Case type * @param List type * @param List key - * @return A new {@link InstanceIdentifierBuilder} + * @return A new {@link Builder} * @throws NullPointerException if any argument is null */ public static & DataObject, - N extends Identifiable & ChildOf, K extends Identifier> - InstanceIdentifierBuilder builder(final Class caze, final Class listItem, final K listKey) { - return new InstanceIdentifierBuilderImpl().addNode(IdentifiableItem.of(caze, listItem, listKey)); + N extends KeyAware & ChildOf, K extends Key> + @NonNull KeyedBuilder builder(final Class caze, final Class listItem, + final K listKey) { + return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey)); + } + + public static > + @NonNull Builder builderOfInherited(final Class root, final Class container) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new RegularBuilder<>(createStep(container)); + } + + public static & DataObject, + T extends ChildOf> + @NonNull Builder builderOfInherited(final Class root, + final Class caze, final Class container) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new RegularBuilder<>(createStep(caze, container)); + } + + public static & ChildOf, + K extends Key> + @NonNull KeyedBuilder builderOfInherited(final Class root, + final Class listItem, final K listKey) { + // FIXME: we are losing root identity, hence namespaces may not work correctly + return new KeyedBuilder<>(new KeyStep<>(listItem, listKey)); + } + + public static & DataObject, + N extends KeyAware & ChildOf, K extends Key> + @NonNull KeyedBuilder 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 KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey)); + } + + @Beta + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static & DataObject> @NonNull DataObjectStep createStep( + final Class caze, final Class type) { + return KeyAware.class.isAssignableFrom(type) ? new KeylessStep(type, caze) : new NodeStep<>(type, caze); + } + + @Beta + public static @NonNull DataObjectStep createStep(final Class type) { + return createStep(null, type); } /** @@ -524,52 +543,52 @@ public class InstanceIdentifier implements Path internalCreate(final Iterable pathArguments) { - final Iterator it = requireNonNull(pathArguments, "pathArguments may not be null") - .iterator(); - final HashCodeBuilder hashBuilder = new HashCodeBuilder<>(); + 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 var hashBuilder = new HashCodeBuilder>(); boolean wildcard = false; - PathArgument arg = null; + DataObjectStep arg; - while (it.hasNext()) { + do { arg = it.next(); - checkArgument(arg != null, "pathArguments may not contain null elements"); + // Non-null is implied by our callers + final var type = verifyNotNull(arg).type(); + checkArgument(ChildOf.class.isAssignableFrom(type) || Augmentation.class.isAssignableFrom(type), + "%s is not a valid path argument", type); - // TODO: sanity check ChildOf<>; hashBuilder.addArgument(arg); - if (Identifiable.class.isAssignableFrom(arg.getType()) && !(arg instanceof IdentifiableItem)) { + if (!(arg instanceof ExactDataObjectStep)) { wildcard = true; } - } - checkArgument(arg != null, "pathArguments may not be empty"); + } while (it.hasNext()); return trustedCreate(arg, pathArguments, hashBuilder.build(), wildcard); } /** - * Create an instance identifier for a very specific object type. + * Create an instance identifier for a sequence of {@link DataObjectStep} steps. The steps are required to be formed + * of classes extending either {@link ChildOf} or {@link Augmentation} contracts. This method does not check whether + * or not the sequence is structurally sound, for example that an {@link Augmentation} follows an + * {@link Augmentable} step. Furthermore the compile-time indicated generic type of the returned object does not + * necessarily match the contained state. * *

- * Example: - *

-     *  List<PathArgument> path = Arrays.asList(new Item(Nodes.class))
-     *  new InstanceIdentifier(path);
-     * 
+ * Failure to observe precautions to validate the list's contents may yield an object which mey be rejected at + * run-time or lead to undefined behaviour. * * @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 NullPointerException if {@code pathArguments} is, or contains an item which is, {@code null} + * @throws IllegalArgumentException if {@code pathArguments} is empty or contains an item which does not represent + * a valid addressing step. */ - public static InstanceIdentifier create(final Iterable pathArguments) { - if (pathArguments instanceof ImmutableCollection) { - @SuppressWarnings("unchecked") - final Iterable immutableArguments = (Iterable) pathArguments; - return internalCreate(immutableArguments); - } - - return internalCreate(ImmutableList.copyOf(pathArguments)); + @SuppressWarnings("unchecked") + public static @NonNull InstanceIdentifier unsafeOf( + final List> pathArguments) { + return (InstanceIdentifier) internalCreate(ImmutableList.copyOf(pathArguments)); } /** @@ -585,9 +604,11 @@ public class InstanceIdentifier implements Path InstanceIdentifier create(final Class type) { - return (InstanceIdentifier) create(ImmutableList.of(Item.of(type))); + public static > @NonNull InstanceIdentifier create( + final Class<@NonNull T> type) { + return (InstanceIdentifier) internalCreate(ImmutableList.of(createStep(type))); } /** @@ -598,7 +619,8 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier> K keyOf( + // FIXME: reconsider naming and design of this method + public static & DataObject, K extends Key> K keyOf( final InstanceIdentifier id) { requireNonNull(id); checkArgument(id instanceof KeyedInstanceIdentifier, "%s does not have a key", id); @@ -609,98 +631,90 @@ public class InstanceIdentifier implements Path 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).getKey(); - } else { - wildcarded = true; - } - - return new KeyedInstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash, key); + static @NonNull InstanceIdentifier trustedCreate(final DataObjectStep lastStep, + final Iterable> pathArguments, final int hash, final boolean wildcarded) { + if (lastStep instanceof NodeStep) { + return new InstanceIdentifier(lastStep.type(), pathArguments, wildcarded, hash); + } else if (lastStep instanceof KeyStep predicate) { + return new KeyedInstanceIdentifier(predicate, pathArguments, wildcarded, hash); + } else if (lastStep instanceof KeylessStep) { + return new InstanceIdentifier(lastStep.type(), pathArguments, true, hash); + } else { + throw new IllegalStateException("Unhandled step " + lastStep); } - - return new InstanceIdentifier(arg.getType(), pathArguments, wildcarded, hash); } - /** - * 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 { + @Deprecated(since = "13.0.0", forRemoval = true) + private abstract static sealed class AbstractPathArgument + implements Comparable>, Serializable { + @java.io.Serial + private static final long serialVersionUID = 1L; + + private final @NonNull Class type; + + AbstractPathArgument(final Class type) { + this.type = requireNonNull(type, "Type may not be null."); + } + /** * Return the data object type backing this PathArgument. * * @return Data object type. */ - Class getType(); + final @NonNull Class type() { + return type; + } /** - * Return an optional enclosing case type. This is used only when {@link #getType()} references a node defined + * Return an optional enclosing case type. This is used only when {@link #type()} 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. + * @return case class or {@code null} */ - default Optional> getCaseType() { - return Optional.empty(); - } - } - - private abstract static class AbstractPathArgument implements PathArgument, Serializable { - private static final long serialVersionUID = 1L; - private final Class type; - - AbstractPathArgument(final Class type) { - this.type = requireNonNull(type, "Type may not be null."); - } - - @Override - public final Class getType() { - return type; + Class caseType() { + return null; } - Object getKey() { + @Nullable Object key() { return null; } @Override public final int hashCode() { - return Objects.hash(type, getCaseType(), getKey()); + return Objects.hash(type, caseType(), key()); } @Override public final boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (!(obj instanceof AbstractPathArgument)) { - return false; - } - final AbstractPathArgument other = (AbstractPathArgument) obj; - return type.equals(other.type) && Objects.equals(getKey(), other.getKey()) - && getCaseType().equals(other.getCaseType()); + return this == obj || obj instanceof AbstractPathArgument other && type.equals(other.type) + && Objects.equals(key(), other.key()) && Objects.equals(caseType(), other.caseType()); } @Override - public final int compareTo(final PathArgument arg) { - final int cmp = compareClasses(type, arg.getType()); + public final int compareTo(final AbstractPathArgument arg) { + final int cmp = compareClasses(type, arg.type()); if (cmp != 0) { return cmp; } - final Optional> caseType = getCaseType(); - if (!caseType.isPresent()) { - return arg.getCaseType().isPresent() ? -1 : 1; + final var caseType = caseType(); + final var argCaseType = arg.caseType(); + if (caseType == null) { + return argCaseType == null ? 1 : -1; } - final Optional> argCaseType = getCaseType(); - return argCaseType.isPresent() ? compareClasses(caseType.get(), argCaseType.get()) : 1; + return argCaseType == null ? 1 : compareClasses(caseType, argCaseType); } private static int compareClasses(final Class first, final Class second) { return first.getCanonicalName().compareTo(second.getCanonicalName()); } + + @java.io.Serial + final Object readResolve() throws ObjectStreamException { + return toStep(); + } + + abstract DataObjectStep toStep(); } /** @@ -709,51 +723,24 @@ public class InstanceIdentifier implements Path Item type */ - public static class Item extends AbstractPathArgument { + @Deprecated(since = "13.0.0", forRemoval = true) + private static sealed class Item extends AbstractPathArgument { + @java.io.Serial private static final long serialVersionUID = 1L; - /** - * Construct an Item. - * - * @param type Backing class - * @deprecated Use {@link #of(Class)} instead. - */ - @Deprecated - 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 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> Item of( - final Class caseType, final Class type) { - return new CaseItem<>(caseType, type); + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + final DataObjectStep toStep() { + return createStep((Class) caseType(), type()); } @Override public String toString() { - return getType().getName(); + return type().getName(); } } @@ -764,75 +751,44 @@ public class InstanceIdentifier implements Path An object that is identifiable by an identifier * @param The identifier of the object */ - public static class IdentifiableItem & DataObject, T extends Identifier> + @Deprecated(since = "13.0.0", forRemoval = true) + private static sealed class IdentifiableItem & DataObject, T extends Key> extends AbstractPathArgument { + @java.io.Serial private static final long serialVersionUID = 1L; - private final T key; - /** - * Construct an Item. - * - * @param type Backing class - * @param key key - * @deprecated Use {@link #of(Class, Identifier)} instead. - */ - @Deprecated - public IdentifiableItem(final Class type, final T key) { + private final @NonNull T key; + + IdentifiableItem(final Class type, final T key) { super(type); this.key = requireNonNull(key, "Key may not be null."); } - /** - * 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> IdentifiableItem of( - final Class type, final I key) { - return new IdentifiableItem<>(type, key); - } - - /** - * 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> 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 final T getKey() { + final @NonNull T key() { return key; } + @Override + final KeyStep toStep() { + return new KeyStep<>(type(), caseType(), key); + } + @Override public String toString() { - return getType().getName() + "[key=" + key + "]"; + return type().getName() + "[key=" + key + "]"; } } + @Deprecated(since = "13.0.0", forRemoval = true) private static final class CaseItem & DataObject, T extends ChildOf> extends Item { + @java.io.Serial private static final long serialVersionUID = 1L; private final Class caseType; @@ -843,13 +799,15 @@ public class InstanceIdentifier implements Path> getCaseType() { - return Optional.of(caseType); + Class caseType() { + return caseType; } } + @Deprecated(since = "13.0.0", forRemoval = true) private static final class CaseIdentifiableItem & DataObject, - T extends ChildOf & Identifiable, K extends Identifier> extends IdentifiableItem { + T extends ChildOf & KeyAware, K extends Key> extends IdentifiableItem { + @java.io.Serial private static final long serialVersionUID = 1L; private final Class caseType; @@ -860,23 +818,74 @@ public class InstanceIdentifier implements Path> getCaseType() { - return Optional.of(caseType); + Class caseType() { + return caseType; } } - public interface InstanceIdentifierBuilder extends Builder> { + /** + * A builder of {@link InstanceIdentifier} objects. + * + * @param Instance identifier target type + */ + public abstract static sealed class Builder { + private final ImmutableList.Builder> pathBuilder; + private final HashCodeBuilder> hashBuilder; + private final Iterable> basePath; + + private boolean wildcard; + + Builder(final Builder prev, final DataObjectStep item) { + pathBuilder = prev.pathBuilder; + hashBuilder = prev.hashBuilder; + basePath = prev.basePath; + wildcard = prev.wildcard; + appendItem(item); + } + + Builder(final InstanceIdentifier identifier) { + pathBuilder = ImmutableList.builder(); + hashBuilder = new HashCodeBuilder<>(identifier.hashCode()); + wildcard = identifier.isWildcarded(); + basePath = identifier.pathArguments; + } + + Builder(final DataObjectStep item, final boolean wildcard) { + pathBuilder = ImmutableList.builder(); + hashBuilder = new HashCodeBuilder<>(); + basePath = null; + hashBuilder.addArgument(item); + pathBuilder.add(item); + this.wildcard = wildcard; + } + + final boolean wildcard() { + return wildcard; + } + /** - * 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 + * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by + * the builder. * - * Example, + * @param container augmentation class + * @param augmentation type + * @return this builder + * @throws NullPointerException if {@code container} is null + */ + public final > Builder augmentation( + final Class container) { + return append(new NodeStep<>(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 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 * @@ -885,14 +894,14 @@ public class InstanceIdentifier implements Path> InstanceIdentifierBuilder child(Class container); + public final > Builder child(final Class container) { + return append(createStep(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. + * 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 @@ -901,14 +910,15 @@ public class InstanceIdentifier implements Path & DataObject, N extends ChildOf> InstanceIdentifierBuilder child( - Class caze, Class container); + public final & DataObject, N extends ChildOf> Builder child( + final Class caze, final Class container) { + return append(createStep(caze, container)); + } /** - * 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 + * 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. * * @param listItem List to append * @param listKey List key @@ -917,14 +927,15 @@ public class InstanceIdentifier implements Path & ChildOf, K extends Identifier> InstanceIdentifierBuilder child( - Class listItem, K listKey); + public final & ChildOf, K extends Key> KeyedBuilder child( + final Class<@NonNull N> listItem, final K listKey) { + return append(new KeyStep<>(listItem, 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. + * 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 caze Choice case class * @param listItem List to append @@ -935,51 +946,128 @@ public class InstanceIdentifier implements Path & DataObject, K extends Identifier, - N extends Identifiable & ChildOf> InstanceIdentifierBuilder child(Class caze, - Class listItem, K listKey); + public final & DataObject, K extends Key, + N extends KeyAware & ChildOf> KeyedBuilder child(final Class caze, + final Class listItem, final K listKey) { + return append(new KeyStep<>(listItem, requireNonNull(caze), listKey)); + } /** - * Build an identifier which refers to a specific augmentation of the current InstanceIdentifier referenced by - * the builder. + * Build the instance identifier. * - * @param container augmentation class - * @param augmentation type - * @return this builder - * @throws NullPointerException if {@code container} is null + * @return Resulting {@link InstanceIdentifier}. */ - > InstanceIdentifierBuilder augmentation(Class container); + public abstract @NonNull InstanceIdentifier build(); + + @Override + public final int hashCode() { + return hashBuilder.build(); + } + + @Override + public final boolean equals(final Object obj) { + return this == obj || obj instanceof Builder other + && wildcard == other.wildcard && hashCode() == other.hashCode() + && Iterables.elementsEqual(pathArguments(), other.pathArguments()); + } + + final Iterable> pathArguments() { + final var args = pathBuilder.build(); + return basePath == null ? args : Iterables.concat(basePath, args); + } + + final void appendItem(final DataObjectStep item) { + hashBuilder.addArgument(item); + pathBuilder.add(item); + if (!(item instanceof ExactDataObjectStep)) { + wildcard = true; + } + } + + abstract @NonNull RegularBuilder append(DataObjectStep step); + + abstract , Y extends Key> @NonNull KeyedBuilder append( + KeyStep step); + } + + public static final class KeyedBuilder, K extends Key> + extends Builder { + private @NonNull KeyStep lastStep; + + KeyedBuilder(final KeyStep firstStep) { + super(firstStep, false); + lastStep = requireNonNull(firstStep); + } + + KeyedBuilder(final KeyedInstanceIdentifier identifier) { + super(identifier); + lastStep = identifier.lastStep(); + } + + private KeyedBuilder(final RegularBuilder prev, final KeyStep lastStep) { + super(prev, lastStep); + this.lastStep = requireNonNull(lastStep); + } /** * Build the instance identifier. * - * @return Resulting instance identifier. + * @return Resulting {@link KeyedInstanceIdentifier}. */ @Override - InstanceIdentifier build(); - } + public @NonNull KeyedInstanceIdentifier build() { + return new KeyedInstanceIdentifier<>(lastStep, pathArguments(), wildcard(), hashCode()); + } + + @Override + @NonNull RegularBuilder append(final DataObjectStep step) { + return new RegularBuilder<>(this, step); + } - private void writeObject(final java.io.ObjectOutputStream out) throws IOException { - out.defaultWriteObject(); - out.writeInt(Iterables.size(pathArguments)); - for (Object o : pathArguments) { - out.writeObject(o); + @Override + @SuppressWarnings("unchecked") + , Y extends Key> KeyedBuilder append(final KeyStep step) { + appendItem(step); + lastStep = (KeyStep) requireNonNull(step); + return (KeyedBuilder) this; } } - private void readObject(final java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { - in.defaultReadObject(); + private static final class RegularBuilder extends Builder { + private @NonNull Class type; + + RegularBuilder(final DataObjectStep item) { + super(item, !(item instanceof ExactDataObjectStep)); + type = item.type(); + } + + RegularBuilder(final InstanceIdentifier identifier) { + super(identifier); + type = identifier.getTargetType(); + } + + private RegularBuilder(final KeyedBuilder prev, final DataObjectStep item) { + super(prev, item); + type = item.type(); + } - final int size = in.readInt(); - final List args = new ArrayList<>(size); - for (int i = 0; i < size; ++i) { - args.add((PathArgument) in.readObject()); + @Override + public InstanceIdentifier build() { + return new InstanceIdentifier<>(type, pathArguments(), wildcard(), hashCode()); } - try { - PATHARGUMENTS_FIELD.set(this, ImmutableList.copyOf(args)); - } catch (IllegalArgumentException | IllegalAccessException e) { - throw new IOException(e); + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + RegularBuilder append(final DataObjectStep step) { + appendItem(step); + type = (Class) step.type(); + return (RegularBuilder) this; + } + + @Override + , Y extends Key> KeyedBuilder append( + final KeyStep item) { + return new KeyedBuilder<>(this, item); } } }