Bump versions to 14.0.0-SNAPSHOT
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / AbstractOpaqueCodecContext.java
1 /*
2  * Copyright (c) 2019 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
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
7  */
8 package org.opendaylight.mdsal.binding.dom.codec.impl;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11 import static java.util.Objects.requireNonNull;
12
13 import com.google.common.base.Throwables;
14 import java.lang.invoke.MethodHandle;
15 import java.lang.invoke.MethodHandles;
16 import java.lang.invoke.MethodType;
17 import javax.xml.transform.dom.DOMSource;
18 import net.bytebuddy.ByteBuddy;
19 import net.bytebuddy.dynamic.DynamicType.Builder;
20 import net.bytebuddy.jar.asm.Opcodes;
21 import org.eclipse.jdt.annotation.NonNull;
22 import org.opendaylight.mdsal.binding.dom.codec.api.BindingOpaqueObjectCodecTreeNode;
23 import org.opendaylight.mdsal.binding.loader.BindingClassLoader;
24 import org.opendaylight.mdsal.binding.loader.BindingClassLoader.GeneratorResult;
25 import org.opendaylight.yangtools.yang.binding.OpaqueData;
26 import org.opendaylight.yangtools.yang.binding.OpaqueObject;
27 import org.opendaylight.yangtools.yang.data.api.schema.ForeignDataNode;
28 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
29 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
30
31 abstract sealed class AbstractOpaqueCodecContext<T extends OpaqueObject<T>> extends ValueNodeCodecContext
32         implements BindingOpaqueObjectCodecTreeNode<T> permits AnydataCodecContext, AnyxmlCodecContext {
33     private static final MethodType CTOR_LOOKUP_TYPE = MethodType.methodType(void.class, OpaqueData.class);
34     private static final MethodType CTOR_INVOKE_TYPE = MethodType.methodType(OpaqueObject.class, OpaqueData.class);
35     @SuppressWarnings("rawtypes")
36     private static final Builder<CodecOpaqueObject> TEMPLATE = new ByteBuddy().subclass(CodecOpaqueObject.class)
37             .modifiers(Opcodes.ACC_PUBLIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC);
38
39     private final AbstractValueCodec<Object, Object> valueCodec = new AbstractValueCodec<>() {
40         @Override
41         protected Object serializeImpl(final Object input) {
42             checkArgument(bindingClass.isInstance(input), "Unexpected input %s", input);
43             // FIXME: this works for DOMSource only, for generalization we need to pass down the object model, too
44             final OpaqueData<?> opaqueData = bindingClass.cast(input).getValue();
45             final Object data = opaqueData.getData();
46             checkArgument(data instanceof DOMSource, "Unexpected data %s", data);
47             return data;
48         }
49
50         @Override
51         protected Object deserializeImpl(final Object input) {
52             checkArgument(input instanceof NormalizedNode, "Unexpected input %s", input);
53             return deserializeObject((NormalizedNode) input);
54         }
55     };
56
57     private final MethodHandle proxyConstructor;
58     private final @NonNull Class<T> bindingClass;
59
60     AbstractOpaqueCodecContext(final DataSchemaNode schema, final String getterName, final Class<T> bindingClass,
61             final BindingClassLoader loader) {
62         super(schema, getterName, null);
63         this.bindingClass = requireNonNull(bindingClass);
64         proxyConstructor = createImpl(loader, bindingClass);
65     }
66
67     @Override
68     public final Class<T> getBindingClass() {
69         return bindingClass;
70     }
71
72     @Override
73     public final T deserialize(final NormalizedNode data) {
74         if (data instanceof ForeignDataNode<?> foreign) {
75             return deserialize(foreign);
76         }
77         throw new IllegalArgumentException("Expected a ForeignDataNode, not " + data.contract().getSimpleName());
78     }
79
80     T deserialize(final ForeignDataNode<?> foreignData) {
81         return bindingClass.cast(createBindingProxy(new ForeignOpaqueData<>(foreignData)));
82     }
83
84     @Override
85     public final ForeignDataNode<?> serialize(final T data) {
86         final OpaqueData<?> opaqueData = data.getValue();
87         return opaqueData instanceof ForeignOpaqueData<?> foreign ? foreign.domData() : serializedData(opaqueData);
88     }
89
90     @Override
91     protected final @NonNull Object deserializeObject(final NormalizedNode normalizedNode) {
92         return deserialize(normalizedNode);
93     }
94
95     @Override
96     ValueCodec<Object, Object> getValueCodec() {
97         return valueCodec;
98     }
99
100     abstract @NonNull ForeignDataNode<?> serializedData(OpaqueData<?> opaqueData);
101
102     @SuppressWarnings("checkstyle:illegalCatch")
103     private OpaqueObject<?> createBindingProxy(final OpaqueData<?> data) {
104         try {
105             return (OpaqueObject<?>) proxyConstructor.invokeExact(data);
106         } catch (final Throwable e) {
107             Throwables.throwIfUnchecked(e);
108             throw new IllegalStateException(e);
109         }
110     }
111
112     private static MethodHandle createImpl(final BindingClassLoader rootLoader, final Class<?> bindingClass) {
113         final Class<?> proxyClass = CodecPackage.CODEC.generateClass(rootLoader, bindingClass,
114             (loader, fqcn, bindingInterface) -> GeneratorResult.of(TEMPLATE
115                 .name(fqcn)
116                 .implement(bindingInterface)
117                 .make()));
118
119         try {
120             return MethodHandles.publicLookup().findConstructor(proxyClass, CTOR_LOOKUP_TYPE).asType(CTOR_INVOKE_TYPE);
121         } catch (IllegalAccessException | NoSuchMethodException e) {
122             throw new LinkageError("Failed to access constructor for prototype " + proxyClass, e);
123         }
124     }
125 }