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 extends @NonNull DataObjectStep>> 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 extends DataObject> 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 super T>, 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 super C>>
@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 super C>> @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 super C>>
@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 extends DataRoot>>, 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 super C>, 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 super R>>
@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 super R> & DataObject, T extends ChildOf super C>>
@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 super R>, 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 super R> & DataObject,
N extends EntryObject & ChildOf super C>, 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 extends DataObjectStep>> 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 extends DataObjectStep>> 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 extends DataObjectStep>> 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 super C>> Builder child(
final Class caze, final Class container) {
return append(DataObjectStep.of(caze, container));
}
@Override
public final & ChildOf super T>, 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 super C>> 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);
}
}
}