import com.google.common.collect.ImmutableCollection;
import com.google.common.collect.ImmutableSet;
import edu.umd.cs.findbugs.annotations.CheckReturnValue;
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.VarHandle;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
-import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
-import java.util.Map;
import java.util.Optional;
import java.util.Set;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingDataContainerCodecTreeNode;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCachingCodec;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingNormalizedNodeCodec;
-import org.opendaylight.mdsal.binding.dom.codec.api.BindingStreamEventWriter;
import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException;
import org.opendaylight.mdsal.binding.dom.codec.api.MissingClassInLoadingStrategyException;
import org.opendaylight.mdsal.binding.dom.codec.api.MissingSchemaException;
import org.opendaylight.mdsal.binding.model.api.Type;
import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType;
-import org.opendaylight.mdsal.binding.runtime.api.RuntimeType;
-import org.opendaylight.mdsal.binding.runtime.api.RuntimeTypeContainer;
import org.opendaylight.yangtools.util.ClassLoaderUtils;
import org.opendaylight.yangtools.yang.binding.Augmentable;
import org.opendaylight.yangtools.yang.binding.Augmentation;
import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.PathArgument;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-abstract sealed class DataContainerCodecContext<D extends BindingObject & DataContainer, T extends RuntimeTypeContainer>
+abstract sealed class DataContainerCodecContext<D extends DataContainer, R extends CompositeRuntimeType,
+ P extends DataContainerPrototype<?, R>>
extends CodecContext implements BindingDataContainerCodecTreeNode<D>
- permits CommonDataObjectCodecContext, RootCodecContext {
+ permits CommonDataObjectCodecContext {
private static final Logger LOG = LoggerFactory.getLogger(DataContainerCodecContext.class);
private static final VarHandle EVENT_STREAM_SERIALIZER;
}
}
+ private final @NonNull P prototype;
private final @NonNull ChildAddressabilitySummary childAddressabilitySummary;
// Accessed via a VarHandle
@SuppressWarnings("unused")
+ @SuppressFBWarnings(value = "UUF_UNUSED_FIELD", justification = "https://github.com/spotbugs/spotbugs/issues/2749")
private volatile DataContainerSerializer eventStreamSerializer;
- DataContainerCodecContext(final T type) {
- childAddressabilitySummary = type instanceof RuntimeType runtimeType
- ? computeChildAddressabilitySummary(runtimeType.statement())
- // BindingRuntimeTypes, does not matter
- : ChildAddressabilitySummary.MIXED;
+ DataContainerCodecContext(final P prototype) {
+ this.prototype = requireNonNull(prototype);
+ childAddressabilitySummary = computeChildAddressabilitySummary(prototype.runtimeType().statement());
+ }
+
+ final @NonNull P prototype() {
+ return prototype;
}
@Override
return childAddressabilitySummary;
}
- protected abstract @NonNull CodecContextFactory factory();
-
- protected abstract @NonNull T type();
-
- /**
- * Returns nested node context using supplied YANG Instance Identifier.
- *
- * @param arg Yang Instance Identifier Argument
- * @return Context of child
- * @throws IllegalArgumentException If supplied argument does not represent valid child.
- */
+ // Non-final for ChoiceCodecContext
@Override
- public abstract CodecContext yangPathArgumentChild(YangInstanceIdentifier.PathArgument arg);
+ public CodecContext yangPathArgumentChild(final YangInstanceIdentifier.PathArgument arg) {
+ CodecContextSupplier supplier;
+ if (arg instanceof NodeIdentifier nodeId) {
+ supplier = yangChildSupplier(nodeId);
+ } else if (arg instanceof NodeIdentifierWithPredicates nip) {
+ supplier = yangChildSupplier(new NodeIdentifier(nip.getNodeType()));
+ } else {
+ supplier = null;
+ }
+ return childNonNull(supplier, arg, "Argument %s is not valid child of %s", arg, getSchema()).getCodecContext();
+ }
+
+ abstract @Nullable CodecContextSupplier yangChildSupplier(@NonNull NodeIdentifier arg);
- /**
- * Returns nested node context using supplied Binding Instance Identifier
- * and adds YANG instance identifiers to supplied list.
- *
- * @param arg Binding Instance Identifier Argument
- * @return Context of child or null if supplied {@code arg} does not represent valid child.
- * @throws IllegalArgumentException If supplied argument does not represent valid child.
- */
@Override
public CommonDataObjectCodecContext<?, ?> bindingPathArgumentChild(final PathArgument arg,
final List<YangInstanceIdentifier.PathArgument> builder) {
}
@Override
- public abstract <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(Class<C> childClass);
+ public final <C extends DataObject> CommonDataObjectCodecContext<C, ?> getStreamChild(final Class<C> childClass) {
+ return childNonNull(streamChild(childClass), childClass,
+ "Child %s is not valid child of %s", getBindingClass(), childClass);
+ }
- /**
- * Returns child context as if it was walked by {@link BindingStreamEventWriter}. This means that to enter case, one
- * must issue getChild(ChoiceClass).getChild(CaseClass).
- *
- * @param childClass child class
- * @return Context of child or Optional.empty is supplied class is not applicable in context.
- */
+ @SuppressWarnings("unchecked")
@Override
- public abstract <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(Class<C> childClass);
+ public final <C extends DataObject> CommonDataObjectCodecContext<C, ?> streamChild(final Class<C> childClass) {
+ final var childProto = streamChildPrototype(requireNonNull(childClass));
+ return childProto == null ? null : (CommonDataObjectCodecContext<C, ?>) childProto.getCodecContext();
+ }
+
+ abstract @Nullable CommonDataObjectCodecPrototype<?> streamChildPrototype(@NonNull Class<?> childClass);
@Override
public String toString() {
return getClass().getSimpleName() + " [" + getBindingClass() + "]";
}
- static final <T extends DataObject, C extends DataContainerCodecContext<T, ?> & BindingNormalizedNodeCodec<T>>
+ static final <T extends DataObject, C extends DataContainerCodecContext<T, ?, ?> & BindingNormalizedNodeCodec<T>>
@NonNull BindingNormalizedNodeCachingCodec<T> createCachingCodec(final C context,
final ImmutableCollection<Class<? extends BindingObject>> cacheSpecifier) {
return cacheSpecifier.isEmpty() ? new NonCachingCodec<>(context)
@CheckReturnValue
private IllegalArgumentException childNullException(final QName child, final String message, final Object... args) {
- final QNameModule module = child.getModule();
- if (!factory().getRuntimeContext().getEffectiveModelContext().findModule(module).isPresent()) {
+ final var module = child.getModule();
+ if (!prototype().contextFactory().getRuntimeContext().modelContext().findModule(module).isPresent()) {
return new MissingSchemaException("Module " + module + " is not present in current schema context.");
}
return new IncorrectNestingException(message, args);
@CheckReturnValue
private @NonNull IllegalArgumentException childNullException(final Class<?> childClass, final String message,
final Object... args) {
- return childNullException(factory().getRuntimeContext(), childClass, message, args);
+ return childNullException(prototype().contextFactory().getRuntimeContext(), childClass, message, args);
}
@CheckReturnValue
// Split out to aid inlining
private DataContainerSerializer loadEventStreamSerializer() {
- final DataContainerSerializer loaded = factory().getEventStreamSerializer(getBindingClass());
+ final DataContainerSerializer loaded = prototype().contextFactory().getEventStreamSerializer(getBindingClass());
final Object witness = EVENT_STREAM_SERIALIZER.compareAndExchangeRelease(this, null, loaded);
return witness == null ? loaded : (DataContainerSerializer) witness;
}
}
}
- // FIXME: MDSAL-780 replace this method with BindingRuntimeTypes-driven logic
- static final Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
- final String prefix) {
- final String methodName = method.getName();
- if ("getClass".equals(methodName) || !methodName.startsWith(prefix) || method.getParameterCount() > 0) {
- return Optional.empty();
- }
-
- final Class<?> returnType = method.getReturnType();
- if (DataContainer.class.isAssignableFrom(returnType)) {
- return optionalDataContainer(returnType);
- } else if (List.class.isAssignableFrom(returnType)) {
- return getYangModeledReturnType(method, 0);
- } else if (Map.class.isAssignableFrom(returnType)) {
- return getYangModeledReturnType(method, 1);
- }
- return Optional.empty();
- }
-
- @SuppressWarnings("checkstyle:illegalCatch")
- private static Optional<Class<? extends DataContainer>> getYangModeledReturnType(final Method method,
- final int parameterOffset) {
- try {
- return ClassLoaderUtils.callWithClassLoader(method.getDeclaringClass().getClassLoader(),
- () -> genericParameter(method.getGenericReturnType(), parameterOffset)
- .flatMap(result -> result instanceof Class ? optionalCast((Class<?>) result) : Optional.empty()));
- } catch (Exception e) {
- /*
- * It is safe to log this this exception on debug, since this
- * method should not fail. Only failures are possible if the
- * runtime / backing.
- */
- LOG.debug("Unable to find YANG modeled return type for {}", method, e);
- }
- return Optional.empty();
- }
-
- private static Optional<java.lang.reflect.Type> genericParameter(final java.lang.reflect.Type type,
- final int offset) {
- if (type instanceof ParameterizedType parameterized) {
- final var parameters = parameterized.getActualTypeArguments();
- if (parameters.length > offset) {
- return Optional.of(parameters[offset]);
- }
- }
- return Optional.empty();
- }
-
- private static Optional<Class<? extends DataContainer>> optionalCast(final Class<?> type) {
- return DataContainer.class.isAssignableFrom(type) ? optionalDataContainer(type) : Optional.empty();
- }
-
- // FIXME: MDSAL-780: remove this method
- static final Optional<Class<? extends DataContainer>> optionalDataContainer(final Class<?> type) {
- return Optional.of(type.asSubclass(DataContainer.class));
- }
-
/**
* Determines if two augmentation classes or case classes represents same data.
*
return opt.orElse(null);
}
-
-
private static @NonNull ChildAddressabilitySummary computeChildAddressabilitySummary(final Object nodeSchema) {
// FIXME: rework this to work on EffectiveStatements
if (nodeSchema instanceof DataNodeContainer contaner) {