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.lang.invoke.MethodHandle;
16 import java.lang.invoke.MethodHandles;
17 import java.lang.invoke.MethodType;
18 import javax.xml.transform.dom.DOMSource;
19 import net.bytebuddy.ByteBuddy;
20 import net.bytebuddy.dynamic.DynamicType.Builder;
21 import net.bytebuddy.jar.asm.Opcodes;
22 import org.eclipse.jdt.annotation.NonNull;
23 import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
24 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
25 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.GeneratorResult;
26 import org.opendaylight.yangtools.concepts.AbstractIllegalArgumentCodec;
27 import org.opendaylight.yangtools.concepts.IllegalArgumentCodec;
28 import org.opendaylight.yangtools.yang.binding.OpaqueData;
29 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
30 import org.opendaylight.yangtools.yang.data.api.schema.AnydataNode;
31 import org.opendaylight.yangtools.yang.data.api.schema.DOMSourceAnyxmlNode;
32 import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
33 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
34 import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
35 import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode;
36 import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode;
37 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
39 abstract class OpaqueNodeCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
40 implements BindingOpaqueObjectCodecTreeNode<T> {
41 static final class Anyxml<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
42 Anyxml(final AnyxmlSchemaNode schema, final String getterName, final Class<T> bindingClass,
43 final CodecClassLoader loader) {
44 super(schema, getterName, bindingClass, loader);
48 ForeignDataNode<?> serializedData(final OpaqueData<?> opaqueData) {
49 final Class<?> model = opaqueData.getObjectModel();
50 verify(DOMSource.class.isAssignableFrom(model), "Cannot just yet support object model %s", model);
51 return Builders.anyXmlBuilder().withNodeIdentifier(getDomPathArgument())
52 .withValue((DOMSource) opaqueData.getData()).build();
56 T deserialize(final ForeignDataNode<?> foreignData) {
57 // Streaming cannot support anything but DOMSource-based AnyxmlNodes.
58 verify(foreignData instanceof DOMSourceAnyxmlNode, "Variable node %s not supported yet", foreignData);
59 return super.deserialize(foreignData);
63 static final class Anydata<T extends OpaqueObject<T>> extends OpaqueNodeCodecContext<T> {
64 Anydata(final AnydataSchemaNode schema, final String getterName, final Class<T> bindingClass,
65 final CodecClassLoader loader) {
66 super(schema, getterName, bindingClass, loader);
70 AnydataNode<?> serializedData(final OpaqueData<?> opaqueData) {
71 return buildAnydata(opaqueData);
74 private <M> @NonNull AnydataNode<M> buildAnydata(final OpaqueData<M> opaqueData) {
75 return Builders.anydataBuilder(opaqueData.getObjectModel()).withNodeIdentifier(getDomPathArgument())
76 .withValue(opaqueData.getData()).build();
80 private static final MethodType CTOR_LOOKUP_TYPE = MethodType.methodType(void.class, OpaqueData.class);
81 private static final MethodType CTOR_INVOKE_TYPE = MethodType.methodType(OpaqueObject.class, OpaqueData.class);
82 @SuppressWarnings("rawtypes")
83 private static final Builder<CodecOpaqueObject> TEMPLATE = new ByteBuddy().subclass(CodecOpaqueObject.class)
84 .modifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC);
86 private final IllegalArgumentCodec<Object, Object> valueCodec = new AbstractIllegalArgumentCodec<>() {
88 protected Object serializeImpl(final Object input) {
89 checkArgument(bindingClass.isInstance(input), "Unexpected input %s", input);
90 // FIXME: this works for DOMSource only, for generalization we need to pass down the object model, too
91 final OpaqueData<?> opaqueData = bindingClass.cast(input).getValue();
92 final Object data = opaqueData.getData();
93 checkArgument(data instanceof DOMSource, "Unexpected data %s", data);
98 protected Object deserializeImpl(final Object input) {
99 checkArgument(input instanceof NormalizedNode, "Unexpected input %s", input);
100 return OpaqueNodeCodecContext.this.deserializeObject((NormalizedNode) input);
104 private final MethodHandle proxyConstructor;
105 private final @NonNull Class<T> bindingClass;
107 OpaqueNodeCodecContext(final DataSchemaNode schema, final String getterName, final Class<T> bindingClass,
108 final CodecClassLoader loader) {
109 super(schema, getterName, null);
110 this.bindingClass = requireNonNull(bindingClass);
111 proxyConstructor = createImpl(loader, bindingClass);
115 public final Class<T> getBindingClass() {
120 public final T deserialize(final NormalizedNode data) {
121 checkArgument(data instanceof ForeignDataNode, "Unexpected value %s", data);
122 return deserialize((ForeignDataNode<?>) data);
125 T deserialize(final ForeignDataNode<?> foreignData) {
126 return bindingClass.cast(createBindingProxy(new ForeignOpaqueData<>(foreignData)));
130 public final ForeignDataNode<?> serialize(final T data) {
131 final OpaqueData<?> opaqueData = data.getValue();
132 return opaqueData instanceof ForeignOpaqueData ? ((ForeignOpaqueData<?>) opaqueData).domData()
133 : serializedData(opaqueData);
137 protected final @NonNull Object deserializeObject(final NormalizedNode normalizedNode) {
138 return deserialize(normalizedNode);
142 IllegalArgumentCodec<Object, Object> getValueCodec() {
146 abstract @NonNull ForeignDataNode<?> serializedData(OpaqueData<?> opaqueData);
148 @SuppressWarnings("checkstyle:illegalCatch")
149 private OpaqueObject<?> createBindingProxy(final OpaqueData<?> data) {
151 return (OpaqueObject<?>) proxyConstructor.invokeExact(data);
152 } catch (final Throwable e) {
153 Throwables.throwIfUnchecked(e);
154 throw new IllegalStateException(e);
158 private static MethodHandle createImpl(final CodecClassLoader rootLoader, final Class<?> bindingClass) {
159 final Class<?> proxyClass = rootLoader.generateClass(bindingClass, "codecImpl",
160 (loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
162 .implement(bindingInterface)
166 return MethodHandles.publicLookup().findConstructor(proxyClass, CTOR_LOOKUP_TYPE).asType(CTOR_INVOKE_TYPE);
167 } catch (IllegalAccessException | NoSuchMethodException e) {
168 throw new LinkageError("Failed to access constructor for prototype " + proxyClass, e);