X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;ds=sidebyside;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FSchemaRootCodecContext.java;h=e438ed54dd090c16d5a7f52207071f556bbb1ae4;hb=8437ccb9f506a241f796d34d6e4fea85ac56959b;hp=35b537b7d91d32a4900982de85e731d8c4d329ee;hpb=7a17eba49deb73733bdbb9579d2edd672bb0d71e;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 35b537b7d9..e438ed54dd 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 @@ -10,6 +10,7 @@ package org.opendaylight.mdsal.binding.dom.codec.impl; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Verify.verify; +import static java.util.Objects.requireNonNull; import com.google.common.base.Throwables; import com.google.common.base.Verify; @@ -17,11 +18,14 @@ import com.google.common.cache.CacheBuilder; import com.google.common.cache.CacheLoader; import com.google.common.cache.LoadingCache; 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 org.eclipse.jdt.annotation.NonNull; +import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.spec.naming.BindingMapping; import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.yangtools.util.ClassLoaderUtils; @@ -44,25 +48,51 @@ 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.Module; import org.opendaylight.yangtools.yang.model.api.NotificationDefinition; import org.opendaylight.yangtools.yang.model.api.RpcDefinition; -import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; -import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils; +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<>() { @Override public DataContainerCodecContext load(final Class key) { if (Notification.class.isAssignableFrom(key)) { - return createNotificationDataContext(key); + checkArgument(key.isInterface(), "Supplied class must be interface."); + 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()); } if (RpcInput.class.isAssignableFrom(key) || RpcOutput.class.isAssignableFrom(key)) { - return createRpcDataContext(key); + 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 String className = BindingMapping.getClassName(qname); + + for (final RpcDefinition 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(BindingMapping.getClassName(potentialQName) + className)) { + final ContainerLike schema = getRpcDataSchema(potential, qname); + checkArgument(schema != null, "Schema for %s does not define input / output.", + potentialQName); + return DataContainerCodecPrototype.from(key, schema, factory()).get(); + } + } + + throw new IllegalArgumentException("Supplied class " + key + " is not valid RPC class."); } return createDataTreeChildContext(key); } @@ -88,7 +118,7 @@ final class SchemaRootCodecContext extends DataContainerCo CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override public DataContainerCodecContext load(final QName qname) { - final DataSchemaNode childSchema = getSchema().getDataChildByName(qname); + final DataSchemaNode childSchema = getSchema().dataChildByName(qname); childNonNull(childSchema, qname, "Argument %s is not valid child of %s", qname, getSchema()); if (childSchema instanceof DataNodeContainer || childSchema instanceof ChoiceSchemaNode) { @SuppressWarnings("unchecked") @@ -105,7 +135,7 @@ final class SchemaRootCodecContext extends DataContainerCo CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override public RpcInputCodec load(final Absolute key) { - final ContainerLike schema = SchemaContextUtil.getRpcDataSchema(getSchema(), key.asSchemaPath()); + final ContainerLike schema = getRpcDataSchema(getSchema(), key); @SuppressWarnings("unchecked") final Class cls = (Class) factory().getRuntimeContext().getClassForSchema(schema); @@ -117,17 +147,18 @@ final class SchemaRootCodecContext extends DataContainerCo CacheBuilder.newBuilder().build(new CacheLoader<>() { @Override public NotificationCodecContext load(final Absolute key) { - final NotificationDefinition schema = SchemaContextUtil.getNotificationSchema(getSchema(), - // FIXME: do not convert here! - key.asSchemaPath()); + 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(schema); + final Class> clz = (Class>) + factory().getRuntimeContext().getClassForSchema((NotificationDefinition) stmt); return getNotification(clz); } }); - private SchemaRootCodecContext(final DataContainerCodecPrototype dataPrototype) { + private SchemaRootCodecContext(final DataContainerCodecPrototype dataPrototype) { super(dataPrototype); } @@ -139,11 +170,9 @@ final class SchemaRootCodecContext extends DataContainerCo * @return A new root node */ static SchemaRootCodecContext create(final CodecContextFactory factory) { - final DataContainerCodecPrototype prototype = DataContainerCodecPrototype.rootPrototype(factory); - return new SchemaRootCodecContext<>(prototype); + return new SchemaRootCodecContext<>(DataContainerCodecPrototype.rootPrototype(factory)); } - @SuppressWarnings("unchecked") @Override public DataContainerCodecContext streamChild(final Class childClass) { @@ -162,7 +191,7 @@ final class SchemaRootCodecContext extends DataContainerCo } @Override - public D deserialize(final NormalizedNode normalizedNode) { + public D deserialize(final NormalizedNode normalizedNode) { throw new UnsupportedOperationException("Could not create Binding data representation for root"); } @@ -170,7 +199,7 @@ final class SchemaRootCodecContext extends DataContainerCo return getOrRethrow(actionsByClass, action); } - NotificationCodecContext getNotification(final Class notification) { + NotificationCodecContext getNotification(final Class> notification) { return (NotificationCodecContext) streamChild((Class)notification); } @@ -186,9 +215,9 @@ final class SchemaRootCodecContext extends DataContainerCo return getOrRethrow(rpcDataByPath, containerPath); } - DataContainerCodecContext createDataTreeChildContext(final Class key) { + DataContainerCodecContext createDataTreeChildContext(final Class key) { final QName qname = BindingReflections.findQName(key); - final DataSchemaNode childSchema = childNonNull(getSchema().getDataChildByName(qname), key, + final DataSchemaNode childSchema = childNonNull(getSchema().dataChildByName(qname), key, "%s is not top-level item.", key); return DataContainerCodecPrototype.from(key, childSchema, factory()).get(); } @@ -223,39 +252,51 @@ final class SchemaRootCodecContext extends DataContainerCo return ((Class) type).asSubclass(target); } - ContainerNodeCodecContext createRpcDataContext(final Class key) { - checkArgument(DataContainer.class.isAssignableFrom(key)); - 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 String className = BindingMapping.getClassName(qname); - - for (final RpcDefinition 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(BindingMapping.getClassName(potentialQName) + className)) { - final ContainerLike schema = SchemaNodeUtils.getRpcDataSchema(potential, qname); - checkArgument(schema != null, "Schema for %s does not define input / output.", potential.getQName()); - return (ContainerNodeCodecContext) DataContainerCodecPrototype.from(key, schema, factory()).get(); - } + /** + * 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"); + switch (requireNonNull(qname, "QName must not be null").getLocalName()) { + case "input": + return rpc.getInput(); + case "output": + return rpc.getOutput(); + default: + throw new IllegalArgumentException("Supplied qname " + qname + + " does not represent rpc input or output."); } - - throw new IllegalArgumentException("Supplied class " + key + " is not valid RPC class."); } - NotificationCodecContext createNotificationDataContext(final Class notificationType) { - checkArgument(Notification.class.isAssignableFrom(notificationType)); - checkArgument(notificationType.isInterface(), "Supplied class must be interface."); - final QName qname = BindingReflections.findQName(notificationType); - final NotificationDefinition schema = getSchema().findNotification(qname).orElseThrow( - () -> new IllegalArgumentException("Supplied " + notificationType + " is not valid notification")); - return new NotificationCodecContext<>(notificationType, schema, factory()); + /** + * 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) { @@ -271,7 +312,7 @@ final class SchemaRootCodecContext extends DataContainerCo } @Override - protected Object deserializeObject(final NormalizedNode normalizedNode) { + protected Object deserializeObject(final NormalizedNode normalizedNode) { throw new UnsupportedOperationException("Unable to deserialize root"); }