2 * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static com.google.common.base.Verify.verify;
12 import static java.util.Objects.requireNonNull;
14 import com.google.common.base.Throwables;
15 import java.io.IOException;
16 import java.lang.invoke.MethodHandle;
17 import java.lang.invoke.MethodHandles;
18 import java.lang.invoke.MethodType;
19 import java.lang.reflect.Constructor;
20 import java.lang.reflect.Method;
21 import javassist.CannotCompileException;
22 import javassist.CtClass;
23 import javassist.Modifier;
24 import javassist.NotFoundException;
25 import javax.xml.transform.dom.DOMSource;
26 import org.eclipse.jdt.annotation.NonNull;
27 import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
28 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
29 import org.opendaylight.mdsal.binding.dom.codec.loader.StaticClassPool;
30 import org.opendaylight.yangtools.concepts.Codec;
31 import org.opendaylight.yangtools.yang.binding.OpaqueData;
32 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
33 import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
34 import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
35 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
36 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
37 import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
38 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
40 abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
41 implements BindingOpaqueObjectCodecTreeNode<T> {
42 static final class AnyXml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
43 AnyXml(final AnyXmlSchemaNode schema, final Method getter, final Class<T> bindingClass,
44 final CodecClassLoader loader) {
45 super(schema, getter, bindingClass, loader);
49 ForeignDataNode<?, ?> serializedData(final OpaqueData<?> opaqueData) {
50 final Class<?> model = opaqueData.getObjectModel();
51 verify(DOMSource.class.isAssignableFrom(model), "Cannot just yet support object model %s", model);
52 return Builders.anyXmlBuilder().withNodeIdentifier(getDomPathArgument())
53 .withValue((DOMSource) opaqueData.getData()).build();
57 private static final CtClass SUPERCLASS = StaticClassPool.findClass(CodecOpaqueObject.class);
58 private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(OpaqueObject.class,
61 private final Codec<Object, Object> valueCodec = new Codec<Object, Object>() {
63 public Object serialize(final Object input) {
64 checkArgument(bindingClass.isInstance(input), "Unexpected input %s", input);
65 // FIXME: this works for DOMSource only, for generalization we need to pass down the object model, too
66 final OpaqueData<?> opaqueData = bindingClass.cast(input).getValue();
67 final Object data = opaqueData.getData();
68 checkArgument(data instanceof DOMSource, "Unexpected data %s", data);
73 public Object deserialize(final Object input) {
74 checkArgument(input instanceof NormalizedNode, "Unexpected input %s", input);
75 return OpaqueNodeCodecContext.this.deserializeObject((NormalizedNode<?, ?>) input);
79 private final MethodHandle proxyConstructor;
80 private final @NonNull Class<T> bindingClass;
82 OpaqueNodeCodecContext(final DataSchemaNode schema, final Method getter, final Class<T> bindingClass,
83 final CodecClassLoader loader) {
84 super(schema, getter, null);
85 this.bindingClass = requireNonNull(bindingClass);
86 proxyConstructor = createImpl(loader, bindingClass);
90 public final Class<T> getBindingClass() {
95 public final T deserialize(final NormalizedNode<?, ?> data) {
96 checkArgument(data instanceof ForeignDataNode, "Unexpected value %s", data);
97 final ForeignDataNode<?, ?> foreignData = (ForeignDataNode<?, ?>) data;
98 // Streaming cannot support anything but DOMSource-based AnyxmlNodes.
99 verify(foreignData instanceof AnyXmlNode, "Variable node %s not supported yet", foreignData);
101 return bindingClass.cast(createBindingProxy(new ForeignOpaqueData<>(foreignData)));
105 public final ForeignDataNode<?, ?> serialize(final T data) {
106 final OpaqueData<?> opaqueData = data.getValue();
107 return opaqueData instanceof ForeignOpaqueData ? ((ForeignOpaqueData<?>) opaqueData).domData()
108 : serializedData(opaqueData);
112 protected final @NonNull Object deserializeObject(final NormalizedNode<?, ?> normalizedNode) {
113 return deserialize(normalizedNode);
117 Codec<Object, Object> getValueCodec() {
121 abstract @NonNull ForeignDataNode<?, ?> serializedData(OpaqueData<?> opaqueData);
123 @SuppressWarnings("checkstyle:illegalCatch")
124 private OpaqueObject<?> createBindingProxy(final OpaqueData<?> data) {
126 return (OpaqueObject<?>) proxyConstructor.invokeExact(data);
127 } catch (final Throwable e) {
128 Throwables.throwIfUnchecked(e);
129 throw new IllegalStateException(e);
133 private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
134 final Class<?> proxyClass;
136 proxyClass = rootLoader.generateSubclass(SUPERCLASS, bindingClass, "codecImpl",
137 (pool, binding, generated) -> {
138 generated.addInterface(binding);
139 generated.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
141 } catch (CannotCompileException | IOException | NotFoundException e) {
142 throw new LinkageError("Failed to instantiate prototype for " + bindingClass, e);
147 ctor = proxyClass.getDeclaredConstructor(OpaqueData.class);
148 } catch (NoSuchMethodException e) {
149 throw new LinkageError("Failed to acquire constructor for prototype " + proxyClass, e);
152 return MethodHandles.publicLookup().unreflectConstructor(ctor).asType(CONSTRUCTOR_TYPE);
153 } catch (IllegalAccessException e) {
154 throw new LinkageError("Failed to access constructor for prototype " + proxyClass, e);