All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opendaylight.yangtools.yang.binding.InstanceIdentifier Maven / Gradle / Ivy

/*
 * Copyright (c) 2013 Cisco Systems, Inc. and others.  All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 */
package org.opendaylight.yangtools.yang.binding;

import 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.VerifyException;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamException;
import java.util.List;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.opendaylight.yangtools.binding.Augmentable;
import org.opendaylight.yangtools.binding.Augmentation;
import org.opendaylight.yangtools.binding.ChildOf;
import org.opendaylight.yangtools.binding.ChoiceIn;
import org.opendaylight.yangtools.binding.DataObject;
import org.opendaylight.yangtools.binding.DataObjectIdentifier;
import org.opendaylight.yangtools.binding.DataObjectReference;
import org.opendaylight.yangtools.binding.DataObjectStep;
import org.opendaylight.yangtools.binding.DataRoot;
import org.opendaylight.yangtools.binding.EntryObject;
import org.opendaylight.yangtools.binding.ExactDataObjectStep;
import org.opendaylight.yangtools.binding.Key;
import org.opendaylight.yangtools.binding.KeyStep;
import org.opendaylight.yangtools.binding.KeylessStep;
import org.opendaylight.yangtools.binding.NodeStep;
import org.opendaylight.yangtools.binding.impl.AbstractDataObjectReference;
import org.opendaylight.yangtools.binding.impl.AbstractDataObjectReferenceBuilder;
import org.opendaylight.yangtools.binding.impl.DataObjectIdentifierImpl;
import org.opendaylight.yangtools.binding.impl.DataObjectReferenceImpl;
import org.opendaylight.yangtools.concepts.HierarchicalIdentifier;

/**
 * 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, *

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

* You can create an instance identifier as follows to get to a node with id "openflow:1": {@code * 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 * * @deprecated Use {@link DataObjectIdentifier} for the {@link #isExact()} case and {@link DataObjectReference} for the * {@link #isWildcarded()} case. */ @Deprecated(since = "14.0.0") public sealed class InstanceIdentifier extends AbstractDataObjectReference> implements HierarchicalIdentifier> permits KeyedInstanceIdentifier { @java.io.Serial private static final long serialVersionUID = 3L; private final boolean wildcarded; InstanceIdentifier(final Iterable> steps, final boolean wildcarded) { super(steps); this.wildcarded = wildcarded; } /** * Return the type of data which this InstanceIdentifier identifies. * * @return Target type */ public final @NonNull Class getTargetType() { return lastStep().type(); } /** * 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(getTargetType()), "Cannot adapt %s to %s", this, target); return (InstanceIdentifier) this; } @Override public final boolean isExact() { return !wildcarded; } @Override @Deprecated(since = "14.0.0", forRemoval = true) public final boolean isWildcarded() { return wildcarded; } @Override protected final Class contract() { return wildcarded ? super.contract() : DataObjectIdentifier.class; } /** * Return an instance identifier trimmed at the first occurrence of a specific component type. * *

* For example let's say an instance identifier was built like so, *

     *      identifier = InstanceIdentifier.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)
     * 
* * @param type component type * @return trimmed instance identifier, or null if the component type * is not present. */ public final @Nullable InstanceIdentifier firstIdentifierOf( final Class<@NonNull I> type) { int count = 1; for (var step : steps()) { if (type.equals(step.type())) { @SuppressWarnings("unchecked") final var ret = (InstanceIdentifier) internalCreate(Iterables.limit(steps(), count)); return ret; } ++count; } return null; } /** * 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 , K extends Key> @Nullable K firstKeyOf( final Class<@NonNull N> listItem) { for (var step : steps()) { if (step instanceof KeyStep keyPredicate && listItem.equals(step.type())) { @SuppressWarnings("unchecked") final var ret = (K) keyPredicate.key(); return ret; } } return null; } /** * Check whether an identifier is contained in this identifier. This is a strict subtree check, which requires all * PathArguments to match exactly. * *

* 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: * {@code * this = /nodes/node/openflow:1 * other = /nodes/node/openflow:2 * } * then this.contains(other) will return false. * * @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) { requireNonNull(other, "other should not be null"); final var oit = other.steps().iterator(); for (var step : steps()) { if (!oit.hasNext()) { return false; } if (!step.equals(oit.next())) { return false; } } return true; } /** * Check whether this instance identifier contains the other identifier after wildcard expansion. This is similar * to {@link #contains(InstanceIdentifier)}, with the exception that a wildcards are assumed to match the their * non-wildcarded PathArgument counterpart. * * @param other Identifier which should be checked for inclusion. * @return true if this identifier contains the other object */ public final boolean containsWildcarded(final InstanceIdentifier other) { requireNonNull(other, "other should not be null"); final var otherSteps = other.steps().iterator(); for (var step : steps()) { if (!otherSteps.hasNext()) { 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 @NonNull InstanceIdentifier childIdentifier(final DataObjectStep arg) { return trustedCreate(arg, concat(steps(), arg), wildcarded); } /** * Create an InstanceIdentifier for a child container. This method is a more efficient equivalent to * {@code builder().child(container).build()}. * * @param container Container to append * @param Container type * @return An InstanceIdentifier. * @throws NullPointerException if {@code container} is null */ public final > @NonNull InstanceIdentifier child( final Class<@NonNull N> container) { return childIdentifier(DataObjectStep.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 & ChildOf, K extends Key> @NonNull KeyedInstanceIdentifier child(final Class<@NonNull N> listItem, final K listKey) { return (KeyedInstanceIdentifier) childIdentifier(new KeyStep<>(listItem, listKey)); } /** * 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(DataObjectStep.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 & DataObject, K extends Key, N extends EntryObject & 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)); } /** * Create an InstanceIdentifier for a child augmentation. This method is a more efficient equivalent to * {@code builder().augmentation(container).build()}. * * @param augmentation Container to append * @param Container type * @return An InstanceIdentifier. * @throws NullPointerException if {@code container} is null */ public final > @NonNull InstanceIdentifier augmentation( final Class<@NonNull A> augmentation) { return childIdentifier(new NodeStep<>(augmentation)); } @Override protected Object toSerialForm() { return new IIv5(this); } @java.io.Serial private void readObject(final ObjectInputStream stream) throws IOException, ClassNotFoundException { throwNSE(); } @java.io.Serial private void readObjectNoData() throws ObjectStreamException { throwNSE(); } @java.io.Serial private void writeObject(final ObjectOutputStream stream) throws IOException { throwNSE(); } @Override public Builder toBuilder() { return new RegularBuilder<>(this); } @Override public DataObjectIdentifier toIdentifier() { return toReference().toIdentifier(); } @Override public final InstanceIdentifier toLegacy() { return this; } /** * Convert this {@link InstanceIdentifier} into its corresponding {@link DataObjectReference}. * * @return A non-InstanceIdentifier {@link DataObjectReference} */ public @NonNull DataObjectReference toReference() { final var steps = steps(); return wildcarded ? new DataObjectReferenceImpl<>(steps) : new DataObjectIdentifierImpl<>(null, steps); } @Override @Deprecated(since = "14.0.0") public Builder builder() { return toBuilder(); } /** * 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 Builder} * @throws NullPointerException if {@code container} is null */ public static >> @NonNull Builder builder( final @NonNull Class container) { return new RegularBuilder<>(DataObjectStep.of(container)); } /** * 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 Builder} * @throws NullPointerException if any argument is null */ public static > & DataObject, T extends ChildOf> @NonNull Builder builder(final @NonNull Class caze, final @NonNull Class container) { return new RegularBuilder<>(DataObjectStep.of(caze, container)); } /** * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link EntryObject}. * * @param listItem list item class * @param listKey key value * @param List type * @param List key * @return A new {@link Builder} * @throws NullPointerException if any argument is null */ public static & ChildOf>, K extends Key> @NonNull KeyedBuilder builder(final Class listItem, final K listKey) { return new KeyedBuilder<>(new KeyStep<>(listItem, listKey)); } /** * Create a {@link Builder} for a specific type of InstanceIdentifier which represents an {@link EntryObject} 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 Builder} * @throws NullPointerException if any argument is null */ public static > & DataObject, N extends EntryObject & ChildOf, K extends Key> @NonNull KeyedBuilder builder(final @NonNull Class caze, final @NonNull Class listItem, final @NonNull K listKey) { return new KeyedBuilder<>(new KeyStep<>(listItem, requireNonNull(caze), listKey)); } public static , T extends ChildOf> @NonNull Builder builderOfInherited(final @NonNull Class root, final @NonNull Class container) { // FIXME: we are losing root identity, hence namespaces may not work correctly return new RegularBuilder<>(DataObjectStep.of(container)); } public static , C extends ChoiceIn & 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<>(DataObjectStep.of(caze, container)); } public static , N extends EntryObject & ChildOf, K extends Key> @NonNull KeyedBuilder builderOfInherited(final @NonNull Class root, final @NonNull Class listItem, final @NonNull K listKey) { // FIXME: we are losing root identity, hence namespaces may not work correctly return new KeyedBuilder<>(new KeyStep<>(listItem, listKey)); } public static , C extends ChoiceIn & DataObject, N extends EntryObject & 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)); } /** * 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 NullPointerException if {@code pathArguments} is null */ 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"); boolean wildcard = false; DataObjectStep arg; do { arg = it.next(); // 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); if (!(arg instanceof ExactDataObjectStep)) { wildcard = true; } } while (it.hasNext()); return trustedCreate(arg, pathArguments, wildcard); } /** * 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. * *

* 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 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. */ @SuppressWarnings("unchecked") public static @NonNull InstanceIdentifier unsafeOf( final List> pathArguments) { return (InstanceIdentifier) internalCreate(ImmutableList.copyOf(pathArguments)); } /** * Create an instance identifier for a very specific object type. * *

* For example *

     *      new InstanceIdentifier(Nodes.class)
     * 
* would create an InstanceIdentifier for an object of type Nodes * * @param type The type of the object which this instance identifier represents * @return InstanceIdentifier instance */ // FIXME: considering removing in favor of always going through a builder @SuppressWarnings("unchecked") public static >> @NonNull InstanceIdentifier create( final Class<@NonNull T> type) { return (InstanceIdentifier) internalCreate(ImmutableList.of(DataObjectStep.of(type))); } /** * Return the key associated with the last component of the specified identifier. * * @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. */ // FIXME: reconsider naming and design of this method public static , K extends Key> 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).key(); return ret; } @SuppressWarnings({ "unchecked", "rawtypes" }) static @NonNull InstanceIdentifier trustedCreate(final DataObjectStep lastStep, final Iterable> pathArguments, final boolean wildcarded) { return switch (lastStep) { case NodeStep cast -> new InstanceIdentifier(pathArguments, wildcarded); case KeyStep cast -> new KeyedInstanceIdentifier(pathArguments, wildcarded); case KeylessStep cast -> new InstanceIdentifier(pathArguments, true); }; } /** * A builder of {@link InstanceIdentifier} objects. * * @param Instance identifier target type */ public abstract static sealed class Builder extends AbstractDataObjectReferenceBuilder { Builder(final Builder prev) { super(prev); } Builder(final InstanceIdentifier identifier) { super(identifier); } Builder(final DataObjectStep item) { super(item); } Builder(final ExactDataObjectStep item) { super(item); } @Override public final > Builder augmentation(final Class augmentation) { return append(new NodeStep<>(augmentation)); } @Override public final > Builder child(final Class container) { return append(DataObjectStep.of(container)); } @Override public final & DataObject, N extends ChildOf> Builder child( final Class caze, final Class container) { return append(DataObjectStep.of(caze, container)); } @Override public final & ChildOf, K extends Key> KeyedBuilder child( final Class<@NonNull N> listItem, final K listKey) { return append(new KeyStep<>(listItem, listKey)); } @Override public final & DataObject, K extends Key, N extends EntryObject & ChildOf> KeyedBuilder child(final Class caze, final Class listItem, final K listKey) { return append(new KeyStep<>(listItem, requireNonNull(caze), listKey)); } @Override public abstract @NonNull InstanceIdentifier build(); @Override protected abstract @NonNull RegularBuilder append(DataObjectStep step); @Override protected abstract , Y extends Key> @NonNull KeyedBuilder append( KeyStep step); } public static final class KeyedBuilder, K extends Key> extends Builder implements DataObjectReference.Builder.WithKey { KeyedBuilder(final KeyStep firstStep) { super(firstStep); } KeyedBuilder(final KeyedInstanceIdentifier identifier) { super(identifier); } private KeyedBuilder(final RegularBuilder prev) { super(prev); } /** * Build the instance identifier. * * @return Resulting {@link KeyedInstanceIdentifier}. */ @Override public @NonNull KeyedInstanceIdentifier build() { return new KeyedInstanceIdentifier<>(buildSteps(), wildcard()); } @Override protected @NonNull RegularBuilder append(final DataObjectStep step) { return new RegularBuilder(this).append(step); } @Override @SuppressWarnings("unchecked") protected , Y extends Key> KeyedBuilder append(final KeyStep step) { appendItem(step); return (KeyedBuilder) this; } } private static final class RegularBuilder extends Builder { RegularBuilder(final DataObjectStep item) { super(item); } RegularBuilder(final InstanceIdentifier identifier) { super(identifier); } private RegularBuilder(final KeyedBuilder prev) { super(prev); } @Override public InstanceIdentifier build() { return new InstanceIdentifier<>(buildSteps(), wildcard()); } @Override @SuppressWarnings("unchecked") protected RegularBuilder append(final DataObjectStep step) { appendItem(step); return (RegularBuilder) this; } @Override protected , Y extends Key> KeyedBuilder append( final KeyStep item) { return new KeyedBuilder<>(this).append(item); } } }