import java.util.Optional;
import java.util.ServiceLoader;
import java.util.concurrent.ExecutionException;
-import java.util.function.BiFunction;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.kohsuke.MetaInfServices;
import org.opendaylight.mdsal.binding.model.api.JavaTypeName;
import org.opendaylight.mdsal.binding.runtime.api.ActionRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext;
-import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeTypes;
import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.ContainerRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.DataRuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.InputRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.ListRuntimeType;
import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
+import org.opendaylight.mdsal.binding.runtime.api.OutputRuntimeType;
import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections;
import org.opendaylight.yangtools.concepts.Immutable;
import org.opendaylight.yangtools.util.ClassLoaderUtils;
import org.opendaylight.yangtools.yang.binding.ChoiceIn;
import org.opendaylight.yangtools.yang.binding.DataContainer;
import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.DataObjectStep;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.opendaylight.yangtools.yang.binding.Key;
import org.opendaylight.yangtools.yang.binding.KeyAware;
import org.opendaylight.yangtools.yang.binding.RpcInput;
import org.opendaylight.yangtools.yang.binding.RpcOutput;
import org.opendaylight.yangtools.yang.binding.YangData;
-import org.opendaylight.yangtools.yang.binding.contract.Naming;
import org.opendaylight.yangtools.yang.common.QName;
-import org.opendaylight.yangtools.yang.common.QNameModule;
import org.opendaylight.yangtools.yang.common.YangDataName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ContainerLike;
import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.TypeAware;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
return new DataContainerSerializer(BindingCodecContext.this, streamers.get(key));
}
});
- private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?>> childrenByClass =
+ private final LoadingCache<Class<? extends DataObject>, DataContainerCodecContext<?, ?, ?>> childrenByClass =
CacheBuilder.newBuilder().build(new CacheLoader<>() {
@Override
- public DataContainerCodecContext<?, ?> load(final Class<? extends DataObject> key) {
+ public DataContainerCodecContext<?, ?, ?> load(final Class<? extends DataObject> key) {
final var childSchema = context.getTypes().bindingChild(JavaTypeName.create(key));
if (childSchema instanceof ContainerLikeRuntimeType containerLike) {
if (childSchema instanceof ContainerRuntimeType container
return list.keyType() == null ? new ListCodecContext<>(key, list, BindingCodecContext.this)
: MapCodecContext.of(key, list, BindingCodecContext.this);
} else if (childSchema instanceof ChoiceRuntimeType choice) {
- return new ChoiceCodecContext<>(key, choice, BindingCodecContext.this);
+ return new ChoiceCodecContext<>(key.asSubclass(ChoiceIn.class), choice, BindingCodecContext.this);
} else if (childSchema == null) {
throw DataContainerCodecContext.childNullException(context, key, "%s is not top-level item.", key);
} else {
});
// FIXME: this could also be a leaf!
- private final LoadingCache<QName, DataContainerCodecContext<?, ?>> childrenByDomArg =
+ private final LoadingCache<QName, DataContainerCodecContext<?, ?, ?>> childrenByDomArg =
CacheBuilder.newBuilder().build(new CacheLoader<>() {
@Override
- public DataContainerCodecContext<?, ?> load(final QName qname) throws ClassNotFoundException {
+ public DataContainerCodecContext<?, ?, ?> load(final QName qname) throws ClassNotFoundException {
final var type = context.getTypes();
final var child = type.schemaTreeChild(qname);
if (child == null) {
final var module = qname.getModule();
- if (context.getEffectiveModelContext().findModule(module).isEmpty()) {
+ if (context.modelContext().findModule(module).isEmpty()) {
throw new MissingSchemaException(
"Module " + module + " is not present in current schema context.");
}
.build(new CacheLoader<>() {
@Override
public NotificationCodecContext<?> load(final Class<?> key) {
- // FIXME: sharpen check to an Notification.class
- checkArgument(key.isInterface(), "Supplied class must be interface.");
-
- // TODO: we should be able to work with bindingChild() instead of schemaTreeChild() here
- final var qname = BindingReflections.findQName(key);
- if (context.getTypes().schemaTreeChild(qname) instanceof NotificationRuntimeType type) {
- return new NotificationCodecContext<>(key, type, BindingCodecContext.this);
+ final var runtimeType = context.getTypes().bindingChild(JavaTypeName.create(key));
+ if (runtimeType instanceof NotificationRuntimeType notification) {
+ return new NotificationCodecContext<>(key, notification, BindingCodecContext.this);
+ } if (runtimeType != null) {
+ throw new IllegalArgumentException(key + " maps to unexpected " + runtimeType);
}
- throw new IllegalArgumentException("Supplied " + key + " is not valid notification");
+ throw new IllegalArgumentException(key + " is not a known class");
}
});
private final LoadingCache<Absolute, NotificationCodecContext<?>> notificationsByPath =
CacheBuilder.newBuilder().build(new CacheLoader<>() {
@Override
public ContainerLikeCodecContext<?> load(final Class<?> key) {
- final BiFunction<BindingRuntimeTypes, QName, Optional<? extends ContainerLikeRuntimeType<?, ?>>> lookup;
- if (RpcInput.class.isAssignableFrom(key)) {
- lookup = BindingRuntimeTypes::findRpcInput;
- } else if (RpcOutput.class.isAssignableFrom(key)) {
- lookup = BindingRuntimeTypes::findRpcOutput;
+ final var runtimeType = context.getTypes().findSchema(JavaTypeName.create(key))
+ .orElseThrow(() -> new IllegalArgumentException(key + " is not a known class"));
+ if (RpcInput.class.isAssignableFrom(key) && runtimeType instanceof InputRuntimeType input) {
+ // FIXME: accurate type
+ return new ContainerLikeCodecContext(key, input, BindingCodecContext.this);
+ } else if (RpcOutput.class.isAssignableFrom(key) && runtimeType instanceof OutputRuntimeType output) {
+ // FIXME: accurate type
+ return new ContainerLikeCodecContext(key, output, BindingCodecContext.this);
} else {
- throw new IllegalArgumentException(key + " does not represent an RPC container");
+ throw new IllegalArgumentException(key + " maps to unexpected " + runtimeType);
}
-
- final QName qname = BindingReflections.findQName(key);
- final QNameModule qnameModule = qname.getModule();
- final Module module = context.getEffectiveModelContext().findModule(qnameModule)
- .orElseThrow(() -> new IllegalArgumentException("Failed to find module for " + qnameModule));
- final String className = Naming.getClassName(qname);
-
- for (var potential : module.getRpcs()) {
- final QName potentialQName = potential.getQName();
- /*
- * Check if rpc and class represents data from same module and then checks if rpc local name
- * produces same class name as class name appended with Input/Output based on QName associated
- * with binding class.
- *
- * FIXME: Rework this to have more precise logic regarding Binding Specification.
- */
- if (key.getSimpleName().equals(Naming.getClassName(potentialQName) + className)) {
- final ContainerLike schema = getRpcDataSchema(potential, qname);
- checkArgument(schema != null, "Schema for %s does not define input / output.", potentialQName);
-
- final var type = lookup.apply(context.getTypes(), potentialQName)
- .orElseThrow(() -> new IllegalArgumentException("Cannot find runtime type for " + key));
-
- // FIXME: accurate type
- return new ContainerLikeCodecContext(key, type, BindingCodecContext.this);
- }
- }
-
- throw new IllegalArgumentException("Supplied class " + key + " is not valid RPC class.");
- }
-
- /**
- * Returns RPC input or output schema based on supplied QName.
- *
- * @param rpc RPC Definition
- * @param qname input or output QName with namespace same as RPC
- * @return input or output schema. Returns null if RPC does not have input/output specified.
- */
- private static @Nullable ContainerLike getRpcDataSchema(final @NonNull RpcDefinition rpc,
- final @NonNull QName qname) {
- requireNonNull(rpc, "Rpc Schema must not be null");
- return switch (requireNonNull(qname, "QName must not be null").getLocalName()) {
- case "input" -> rpc.getInput();
- case "output" -> rpc.getOutput();
- default -> throw new IllegalArgumentException(
- "Supplied qname " + qname + " does not represent rpc input or output.");
- };
}
});
private final LoadingCache<Absolute, RpcInputCodec<?>> rpcDataByPath =
return new BindingToNormalizedStreamWriter(getActionCodec(action).output(), domWriter);
}
- @NonNull DataContainerCodecContext<?,?> getCodecContextNode(final InstanceIdentifier<?> binding,
+ @NonNull DataContainerCodecContext<?, ?, ?> getCodecContextNode(final InstanceIdentifier<?> binding,
final List<PathArgument> builder) {
final var it = binding.getPathArguments().iterator();
- final var arg = it.next();
-
- DataContainerCodecContext<?, ?> current;
- final var caseType = arg.getCaseType();
- if (caseType.isPresent()) {
- final @NonNull Class<? extends DataObject> type = caseType.orElseThrow();
- final var choice = choicesByClass.getUnchecked(type);
- choice.addYangPathArgument(arg, builder);
- final var caze = choice.getStreamChild(type);
- caze.addYangPathArgument(arg, builder);
- current = caze.bindingPathArgumentChild(arg, builder);
+ final var step = it.next();
+
+ final DataContainerCodecContext<?, ?, ?> start;
+ final var caseType = step.caseType();
+ if (caseType != null) {
+ final var choice = choicesByClass.getUnchecked(caseType);
+ choice.addYangPathArgument(step, builder);
+ final var caze = choice.getStreamChild(caseType);
+ caze.addYangPathArgument(step, builder);
+ start = caze.bindingPathArgumentChild(step, builder);
} else {
- final var child = getStreamChild(arg.getType());
- child.addYangPathArgument(arg, builder);
- current = child;
+ final var child = getStreamChild(step.type());
+ child.addYangPathArgument(step, builder);
+ start = child;
}
+ var current = start;
while (it.hasNext()) {
current = current.bindingPathArgumentChild(it.next(), builder);
}
* @throws IllegalArgumentException if {@code dom} is empty
*/
@Nullable BindingDataObjectCodecTreeNode<?> getCodecContextNode(final @NonNull YangInstanceIdentifier dom,
- final @Nullable Collection<InstanceIdentifier.PathArgument> bindingArguments) {
+ final @Nullable Collection<DataObjectStep<?>> bindingArguments) {
final var it = dom.getPathArguments().iterator();
if (!it.hasNext()) {
throw new IllegalArgumentException("Path may not be empty");
final Class<?> valueType = method.getReturnType();
final ValueCodec<Object, Object> codec = getCodec(valueType, leafSchema.getType());
valueNode = LeafNodeCodecContext.of(leafSchema, codec, method.getName(), valueType,
- context.getEffectiveModelContext());
+ context.modelContext());
} else if (schema instanceof LeafListSchemaNode leafListSchema) {
final Optional<Type> optType = ClassLoaderUtils.getFirstGenericParameter(
method.getGenericReturnType());
@Override
@SuppressWarnings("unchecked")
- public <E extends DataObject> CommonDataObjectCodecContext<E, ?> getStreamChild(final Class<E> childClass) {
+ public <E extends DataObject> DataContainerCodecContext<E, ?, ?> getStreamChild(final Class<E> childClass) {
final var result = Notification.class.isAssignableFrom(childClass) ? getNotificationContext(childClass)
: getOrRethrow(childrenByClass, childClass);
- return (CommonDataObjectCodecContext<E, ?>) result;
+ return (DataContainerCodecContext<E, ?, ?>) result;
}
@Override
return null;
}
- final var builder = new ArrayList<InstanceIdentifier.PathArgument>();
+ final var builder = new ArrayList<DataObjectStep<?>>();
final var codec = getCodecContextNode(path, builder);
if (codec == null) {
if (data != null) {