X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FSchemaRootCodecContext.java;h=29bbebf469e3cc52de1fbefae5a4c862c2bce3e2;hb=refs%2Fchanges%2F45%2F98245%2F100;hp=bfa286ac54267f1aea8728fc8883c6a4c1636043;hpb=60039d4d027e172f6e1d644fb1c2fa60688c06c0;p=mdsal.git diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java index bfa286ac54..29bbebf469 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/SchemaRootCodecContext.java @@ -21,11 +21,22 @@ import com.google.common.util.concurrent.UncheckedExecutionException; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; -import java.util.Iterator; import java.util.List; import java.util.Optional; +import java.util.function.BiFunction; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException; +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.CompositeRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.ContainerLikeRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.DataRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.RuntimeType; import org.opendaylight.mdsal.binding.spec.naming.BindingMapping; import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.yangtools.util.ClassLoaderUtils; @@ -43,19 +54,15 @@ import org.opendaylight.yangtools.yang.common.QNameModule; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.ActionDefinition; 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.EffectiveModelContext; +import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; import org.opendaylight.yangtools.yang.model.api.Module; -import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; -import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; -final class SchemaRootCodecContext extends DataContainerCodecContext { +final class SchemaRootCodecContext extends DataContainerCodecContext { private final LoadingCache, DataContainerCodecContext> childrenByClass = CacheBuilder.newBuilder().build(new CacheLoader<>() { @@ -86,10 +93,13 @@ final class SchemaRootCodecContext extends DataContainerCo @Override public NotificationCodecContext load(final Class key) { checkArgument(key.isInterface(), "Supplied class must be interface."); + + // TODO: we should be able to work with bindingChild() instead of schemaTreeChild() here final QName qname = BindingReflections.findQName(key); - final NotificationDefinition schema = getSchema().findNotification(qname).orElseThrow( - () -> new IllegalArgumentException("Supplied " + key + " is not valid notification")); - return new NotificationCodecContext<>(key, schema, factory()); + final RuntimeType child = getType().schemaTreeChild(qname); + checkArgument(child instanceof NotificationRuntimeType, "Supplied %s is not valid notification", + key); + return new NotificationCodecContext<>(key, (NotificationRuntimeType) child, factory()); } }); @@ -97,10 +107,22 @@ final class SchemaRootCodecContext extends DataContainerCo .build(new CacheLoader, ContainerNodeCodecContext>() { @Override public ContainerNodeCodecContext load(final Class key) { + final BiFunction>> lookup; + if (RpcInput.class.isAssignableFrom(key)) { + lookup = BindingRuntimeTypes::findRpcInput; + } else if (RpcOutput.class.isAssignableFrom(key)) { + lookup = BindingRuntimeTypes::findRpcOutput; + } else { + throw new IllegalArgumentException(key + " does not represent an RPC container"); + } + + final CodecContextFactory factory = factory(); + final BindingRuntimeContext context = factory.getRuntimeContext(); + final QName qname = BindingReflections.findQName(key); final QNameModule qnameModule = qname.getModule(); - final Module module = getSchema().findModule(qnameModule).orElseThrow( - () -> new IllegalArgumentException("Failed to find module for " + qnameModule)); + final Module module = context.getEffectiveModelContext().findModule(qnameModule) + .orElseThrow(() -> new IllegalArgumentException("Failed to find module for " + qnameModule)); final String className = BindingMapping.getClassName(qname); for (final RpcDefinition potential : module.getRpcs()) { @@ -115,8 +137,12 @@ final class SchemaRootCodecContext extends DataContainerCo if (key.getSimpleName().equals(BindingMapping.getClassName(potentialQName) + className)) { final ContainerLike schema = getRpcDataSchema(potential, qname); checkArgument(schema != null, "Schema for %s does not define input / output.", potentialQName); - return (ContainerNodeCodecContext) DataContainerCodecPrototype.from(key, schema, factory()) - .get(); + + final ContainerLikeRuntimeType type = lookup.apply(context.getTypes(), potentialQName) + .orElseThrow(() -> new IllegalArgumentException("Cannot find runtime type for " + key)); + + return (ContainerNodeCodecContext) DataContainerCodecPrototype.from(key, + (ContainerLikeRuntimeType) type, factory).get(); } } @@ -127,14 +153,19 @@ final class SchemaRootCodecContext extends DataContainerCo private final LoadingCache> childrenByQName = CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override - public DataContainerCodecContext load(final QName qname) { - final DataSchemaNode childSchema = getSchema().dataChildByName(qname); - childNonNull(childSchema, qname, "Argument %s is not valid child of %s", qname, getSchema()); + public DataContainerCodecContext load(final QName qname) throws ClassNotFoundException { + final var type = getType(); + final var child = childNonNull(type.schemaTreeChild(qname), qname, + "Argument %s is not valid child of %s", qname, type); + if (!(child instanceof DataRuntimeType)) { + throw IncorrectNestingException.create("Argument %s is not valid data tree child of %s", qname, + type); + } + + // TODO: improve this check? + final var childSchema = child.statement(); if (childSchema instanceof DataNodeContainer || childSchema instanceof ChoiceSchemaNode) { - @SuppressWarnings("unchecked") - final Class childCls = (Class) - factory().getRuntimeContext().getClassForSchema(childSchema); - return streamChild(childCls); + return streamChild(factory().getRuntimeContext().loadClass(child.javaType())); } throw new UnsupportedOperationException("Unsupported child type " + childSchema.getClass()); @@ -145,11 +176,22 @@ final class SchemaRootCodecContext extends DataContainerCo CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override public RpcInputCodec load(final Absolute key) { - final ContainerLike schema = getRpcDataSchema(getSchema(), key); - @SuppressWarnings("unchecked") - final Class cls = (Class) - factory().getRuntimeContext().getClassForSchema(schema); - return getRpc(cls); + final var rpcName = key.firstNodeIdentifier(); + final var context = factory().getRuntimeContext(); + + final Class container; + switch (key.lastNodeIdentifier().getLocalName()) { + case "input": + container = context.getRpcInput(rpcName); + break; + case "output": + container = context.getRpcOutput(rpcName); + break; + default: + throw new IllegalArgumentException("Unhandled path " + key); + } + + return getRpc(container); } }); @@ -157,18 +199,14 @@ final class SchemaRootCodecContext extends DataContainerCo CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override public NotificationCodecContext load(final Absolute key) { - final SchemaTreeEffectiveStatement stmt = getSchema().findSchemaTreeNode(key) - .orElseThrow(() -> new IllegalArgumentException("Cannot find statement at " + key)); - checkArgument(stmt instanceof NotificationDefinition, "Statement %s is not a notification", stmt); - - @SuppressWarnings("unchecked") - final Class> clz = (Class>) - factory().getRuntimeContext().getClassForSchema((NotificationDefinition) stmt); - return getNotification(clz); + final Class cls = factory().getRuntimeContext().getClassForSchema(key); + checkArgument(Notification.class.isAssignableFrom(cls), "Path %s does not represent a notification", + key); + return getNotificationImpl(cls); } }); - private SchemaRootCodecContext(final DataContainerCodecPrototype dataPrototype) { + private SchemaRootCodecContext(final DataContainerCodecPrototype dataPrototype) { super(dataPrototype); } @@ -183,6 +221,11 @@ final class SchemaRootCodecContext extends DataContainerCo return new SchemaRootCodecContext<>(DataContainerCodecPrototype.rootPrototype(factory)); } + @Override + public WithStatus getSchema() { + return getType().getEffectiveModelContext(); + } + @SuppressWarnings("unchecked") @Override public DataContainerCodecContext streamChild(final Class childClass) { @@ -209,12 +252,16 @@ final class SchemaRootCodecContext extends DataContainerCo return getOrRethrow(actionsByClass, action); } + NotificationCodecContext getNotification(final Absolute notification) { + return getOrRethrow(notificationsByPath, notification); + } + NotificationCodecContext getNotification(final Class> notification) { - return getOrRethrow(notificationsByClass, notification); + return getNotificationImpl(notification); } - NotificationCodecContext getNotification(final Absolute notification) { - return getOrRethrow(notificationsByPath, notification); + private NotificationCodecContext getNotificationImpl(final Class notification) { + return getOrRethrow(notificationsByClass, notification); } ContainerNodeCodecContext getRpc(final Class rpcInputOrOutput) { @@ -226,10 +273,12 @@ final class SchemaRootCodecContext extends DataContainerCo } DataContainerCodecContext createDataTreeChildContext(final Class key) { - final QName qname = BindingReflections.findQName(key); - final DataSchemaNode childSchema = childNonNull(getSchema().dataChildByName(qname), key, + final RuntimeType childSchema = childNonNull(getType().bindingChild(JavaTypeName.create(key)), key, "%s is not top-level item.", key); - return DataContainerCodecPrototype.from(key, childSchema, factory()).get(); + if (childSchema instanceof CompositeRuntimeType && childSchema instanceof DataRuntimeType) { + return DataContainerCodecPrototype.from(key, (CompositeRuntimeType) childSchema, factory()).get(); + } + throw IncorrectNestingException.create("%s is not a valid data tree child of %s", key, this); } ActionCodecContext createActionContext(final Class> action) { @@ -249,12 +298,12 @@ final class SchemaRootCodecContext extends DataContainerCo final ParameterizedType paramType = optParamType.get(); final Type[] args = paramType.getActualTypeArguments(); checkArgument(args.length == expectedArgsLength, "Unexpected (%s) Action generatic arguments", args.length); - final ActionDefinition schema = factory().getRuntimeContext().getActionDefinition(action); + final ActionRuntimeType schema = factory().getRuntimeContext().getActionDefinition(action); return new ActionCodecContext( - DataContainerCodecPrototype.from(asClass(args[inputOffset], RpcInput.class), schema.getInput(), - factory()).get(), - DataContainerCodecPrototype.from(asClass(args[outputOffset], RpcOutput.class), schema.getOutput(), - factory()).get()); + DataContainerCodecPrototype.from(asClass(args[inputOffset], RpcInput.class), schema.input(), + factory()).get(), + DataContainerCodecPrototype.from(asClass(args[outputOffset], RpcOutput.class), schema.output(), + factory()).get()); } private static Class asClass(final Type type, final Class target) { @@ -269,6 +318,8 @@ final class SchemaRootCodecContext extends DataContainerCo * @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. */ + @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", + justification = "https://github.com/spotbugs/spotbugs/issues/811") private static @Nullable ContainerLike getRpcDataSchema(final @NonNull RpcDefinition rpc, final @NonNull QName qname) { requireNonNull(rpc, "Rpc Schema must not be null"); @@ -283,40 +334,14 @@ final class SchemaRootCodecContext extends DataContainerCo } } - /** - * Returns RPC Input or Output Data container from RPC definition. - * - * @param schema SchemaContext in which lookup should be performed. - * @param path Schema path of RPC input/output data container - * @return Notification schema or null, if notification is not present in schema context. - */ - @SuppressFBWarnings(value = "UPM_UNCALLED_PRIVATE_METHOD", - justification = "https://github.com/spotbugs/spotbugs/issues/811") - private static @Nullable ContainerLike getRpcDataSchema(final @NonNull EffectiveModelContext schema, - final @NonNull Absolute path) { - requireNonNull(schema, "Schema context must not be null."); - requireNonNull(path, "Schema path must not be null."); - final Iterator it = path.getNodeIdentifiers().iterator(); - checkArgument(it.hasNext(), "Rpc must have QName."); - final QName rpcName = it.next(); - checkArgument(it.hasNext(), "input or output must be part of path."); - final QName inOrOut = it.next(); - for (final RpcDefinition potential : schema.getOperations()) { - if (rpcName.equals(potential.getQName())) { - return getRpcDataSchema(potential, inOrOut); - } - } - return null; - } - ChoiceNodeCodecContext createChoiceDataContext(final Class caseType) { final Class choiceClass = findCaseChoice(caseType); checkArgument(choiceClass != null, "Class %s is not a valid case representation", caseType); - final DataSchemaNode schema = factory().getRuntimeContext().getSchemaDefinition(choiceClass); - checkArgument(schema instanceof ChoiceSchemaNode, "Class %s does not refer to a choice", caseType); + final CompositeRuntimeType schema = factory().getRuntimeContext().getSchemaDefinition(choiceClass); + checkArgument(schema instanceof ChoiceRuntimeType, "Class %s does not refer to a choice", caseType); - final DataContainerCodecContext choice = DataContainerCodecPrototype.from(choiceClass, - (ChoiceSchemaNode)schema, factory()).get(); + final DataContainerCodecContext choice = DataContainerCodecPrototype.from(choiceClass, + (ChoiceRuntimeType)schema, factory()).get(); Verify.verify(choice instanceof ChoiceNodeCodecContext); return (ChoiceNodeCodecContext) choice; }