import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
-import java.lang.reflect.InvocationHandler;
-import java.lang.reflect.Method;
-import java.lang.reflect.Proxy;
import javax.xml.transform.dom.DOMSource;
+import net.bytebuddy.ByteBuddy;
+import net.bytebuddy.dynamic.DynamicType.Builder;
+import net.bytebuddy.jar.asm.Opcodes;
import org.eclipse.jdt.annotation.NonNull;
import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
-import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
+import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
+import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
+import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
import org.opendaylight.yangtools.yang.binding.OpaqueData;
import org.opendaylight.yangtools.yang.binding.OpaqueObject;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
+import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
+import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
implements BindingOpaqueObjectCodecTreeNode<T> {
- static final class AnyXml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
- AnyXml(final AnyXmlSchemaNode schema, final Method getter, final Class<T> bindingClass) {
- super(schema, getter, bindingClass);
+ static final class Anyxml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
+ Anyxml(final AnyxmlSchemaNode schema, final String getterName, final Class<T> bindingClass,
+ final CodecClassLoader loader) {
+ super(schema, getterName, bindingClass, loader);
}
@Override
- ForeignDataNode<?, ?> serializedData(final OpaqueData<?> opaqueData) {
+ ForeignDataNode<?> serializedData(final OpaqueData<?> opaqueData) {
final Class<?> model = opaqueData.getObjectModel();
verify(DOMSource.class.isAssignableFrom(model), "Cannot just yet support object model %s", model);
return Builders.anyXmlBuilder().withNodeIdentifier(getDomPathArgument())
.withValue((DOMSource) opaqueData.getData()).build();
}
+
+ @Override
+ T deserialize(final ForeignDataNode<?> foreignData) {
+ // Streaming cannot support anything but DOMSource-based AnyxmlNodes.
+ verify(foreignData instanceof DOMSourceAnyxmlNode, "Variable node %s not supported yet", foreignData);
+ return super.deserialize(foreignData);
+ }
+ }
+
+ static final class Anydata<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
+ Anydata(final AnydataSchemaNode schema, final String getterName, final Class<T> bindingClass,
+ final CodecClassLoader loader) {
+ super(schema, getterName, bindingClass, loader);
+ }
+
+ @Override
+ AnydataNode<?> serializedData(final OpaqueData<?> opaqueData) {
+ return buildAnydata(opaqueData);
+ }
+
+ private <M> @NonNull AnydataNode<M> buildAnydata(final OpaqueData<M> opaqueData) {
+ return Builders.anydataBuilder(opaqueData.getObjectModel()).withNodeIdentifier(getDomPathArgument())
+ .withValue(opaqueData.getData()).build();
+ }
}
- private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, InvocationHandler.class);
- private static final MethodType OPAQUEOBJECT_TYPE = MethodType.methodType(OpaqueObject.class,
- ForeignOpaqueObject.class);
+ private static final MethodType CTOR_LOOKUP_TYPE = MethodType.methodType(void.class, OpaqueData.class);
+ private static final MethodType CTOR_INVOKE_TYPE = MethodType.methodType(OpaqueObject.class, OpaqueData.class);
+ @SuppressWarnings("rawtypes")
+ private static final Builder<CodecOpaqueObject> TEMPLATE = new ByteBuddy().subclass(CodecOpaqueObject.class)
+ .modifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC);
- private final Codec<Object, Object> valueCodec = new Codec<Object, Object>() {
+ private final IllegalArgumentCodec<Object, Object> valueCodec = new AbstractIllegalArgumentCodec<>() {
@Override
- public Object serialize(final Object input) {
+ protected Object serializeImpl(final Object input) {
checkArgument(bindingClass.isInstance(input), "Unexpected input %s", input);
// FIXME: this works for DOMSource only, for generalization we need to pass down the object model, too
final OpaqueData<?> opaqueData = bindingClass.cast(input).getValue();
}
@Override
- public Object deserialize(final Object input) {
+ protected Object deserializeImpl(final Object input) {
checkArgument(input instanceof NormalizedNode, "Unexpected input %s", input);
- return OpaqueNodeCodecContext.this.deserializeObject((NormalizedNode<?, ?>) input);
+ return OpaqueNodeCodecContext.this.deserializeObject((NormalizedNode) input);
}
};
private final MethodHandle proxyConstructor;
private final @NonNull Class<T> bindingClass;
- OpaqueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Class<T> bindingClass) {
- super(schema, getter, null);
+ OpaqueNodeCodecContext(final DataSchemaNode schema, final String getterName, final Class<T> bindingClass,
+ final CodecClassLoader loader) {
+ super(schema, getterName, null);
this.bindingClass = requireNonNull(bindingClass);
-
- final Class<?> proxyClass = Proxy.getProxyClass(bindingClass.getClassLoader(), bindingClass);
- try {
- proxyConstructor = MethodHandles.publicLookup().findConstructor(proxyClass, CONSTRUCTOR_TYPE)
- .asType(OPAQUEOBJECT_TYPE);
- } catch (NoSuchMethodException | IllegalAccessException e) {
- throw new IllegalStateException("Failed to find contructor for class " + proxyClass, e);
- }
+ proxyConstructor = createImpl(loader, bindingClass);
}
@Override
}
@Override
- public final T deserialize(final NormalizedNode<?, ?> data) {
+ public final T deserialize(final NormalizedNode data) {
checkArgument(data instanceof ForeignDataNode, "Unexpected value %s", data);
- final ForeignDataNode<?, ?> foreignData = (ForeignDataNode<?, ?>) data;
- // Streaming cannot support anything but DOMSource-based AnyxmlNodes.
- verify(foreignData instanceof AnyXmlNode, "Variable node %s not supported yet", foreignData);
+ return deserialize((ForeignDataNode<?>) data);
+ }
- final ForeignOpaqueData<?> opaqueData = new ForeignOpaqueData<>(foreignData);
- return bindingClass.cast(createBindingProxy(new ForeignOpaqueObject<>(bindingClass, opaqueData)));
+ T deserialize(final ForeignDataNode<?> foreignData) {
+ return bindingClass.cast(createBindingProxy(new ForeignOpaqueData<>(foreignData)));
}
@Override
- public final ForeignDataNode<?, ?> serialize(final T data) {
+ public final ForeignDataNode<?> serialize(final T data) {
final OpaqueData<?> opaqueData = data.getValue();
return opaqueData instanceof ForeignOpaqueData ? ((ForeignOpaqueData<?>) opaqueData).domData()
: serializedData(opaqueData);
}
@Override
- protected final @NonNull Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
+ protected final @NonNull Object deserializeObject(final NormalizedNode normalizedNode) {
return deserialize(normalizedNode);
}
@Override
- Codec<Object, Object> getValueCodec() {
+ IllegalArgumentCodec<Object, Object> getValueCodec() {
return valueCodec;
}
- abstract @NonNull ForeignDataNode<?, ?> serializedData(OpaqueData<?> opaqueData);
+ abstract @NonNull ForeignDataNode<?> serializedData(OpaqueData<?> opaqueData);
@SuppressWarnings("checkstyle:illegalCatch")
- private OpaqueObject<?> createBindingProxy(final ForeignOpaqueObject<?> handler) {
+ private OpaqueObject<?> createBindingProxy(final OpaqueData<?> data) {
try {
- return (OpaqueObject<?>) proxyConstructor.invokeExact(handler);
+ return (OpaqueObject<?>) proxyConstructor.invokeExact(data);
} catch (final Throwable e) {
Throwables.throwIfUnchecked(e);
throw new IllegalStateException(e);
}
}
+
+ private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
+ final Class<?> proxyClass = rootLoader.generateClass(bindingClass, "codecImpl",
+ (loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
+ .name(fqcn)
+ .implement(bindingInterface)
+ .make()));
+
+ try {
+ return MethodHandles.publicLookup().findConstructor(proxyClass, CTOR_LOOKUP_TYPE).asType(CTOR_INVOKE_TYPE);
+ } catch (IllegalAccessException | NoSuchMethodException e) {
+ throw new LinkageError("Failed to access constructor for prototype " + proxyClass, e);
+ }
+ }
}