Convert mdsal-binding-dom-codec to a JPMS module
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / NotificationCodecContext.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. 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.checkState;
11 import static com.google.common.base.Verify.verifyNotNull;
12 import static net.bytebuddy.implementation.bytecode.member.MethodVariableAccess.loadThis;
13 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.getField;
14 import static org.opendaylight.mdsal.binding.dom.codec.impl.ByteBuddyUtils.putField;
15
16 import com.google.common.base.Throwables;
17 import java.lang.invoke.MethodHandle;
18 import java.lang.invoke.MethodHandles;
19 import java.lang.invoke.MethodType;
20 import java.time.Instant;
21 import net.bytebuddy.ByteBuddy;
22 import net.bytebuddy.description.method.MethodDescription;
23 import net.bytebuddy.description.method.MethodDescription.InGenericShape;
24 import net.bytebuddy.description.type.TypeDefinition;
25 import net.bytebuddy.description.type.TypeDescription;
26 import net.bytebuddy.description.type.TypeDescription.Generic;
27 import net.bytebuddy.dynamic.scaffold.InstrumentedType;
28 import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
29 import net.bytebuddy.implementation.Implementation;
30 import net.bytebuddy.implementation.bytecode.ByteCodeAppender;
31 import net.bytebuddy.implementation.bytecode.StackManipulation;
32 import net.bytebuddy.implementation.bytecode.member.MethodInvocation;
33 import net.bytebuddy.implementation.bytecode.member.MethodReturn;
34 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
35 import net.bytebuddy.jar.asm.Opcodes;
36 import net.bytebuddy.matcher.ElementMatchers;
37 import org.eclipse.jdt.annotation.NonNull;
38 import org.opendaylight.mdsal.binding.dom.codec.impl.loader.CodecClassLoader.GeneratorResult;
39 import org.opendaylight.mdsal.binding.runtime.api.NotificationRuntimeType;
40 import org.opendaylight.yangtools.yang.binding.BaseNotification;
41 import org.opendaylight.yangtools.yang.binding.DataObject;
42 import org.opendaylight.yangtools.yang.binding.EventInstantAware;
43 import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
44 import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer;
45 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
46
47 final class NotificationCodecContext<D extends DataObject & BaseNotification>
48         extends DataObjectCodecContext<D, NotificationRuntimeType> {
49     private static final Generic EVENT_INSTANT_AWARE = TypeDefinition.Sort.describe(EventInstantAware.class);
50
51     private static final String EVENT_INSTANT_NAME;
52     private static final Generic EVENT_INSTANT_RETTYPE;
53
54     static {
55         final MethodDescription eventInstance = EVENT_INSTANT_AWARE.getDeclaredMethods().getOnly();
56         EVENT_INSTANT_NAME = eventInstance.getName();
57         EVENT_INSTANT_RETTYPE = eventInstance.getReturnType();
58     }
59
60     private static final Generic BB_DOCC = TypeDefinition.Sort.describe(DataObjectCodecContext.class);
61     private static final Generic BB_DNC = TypeDefinition.Sort.describe(DistinctNodeContainer.class);
62     private static final Generic BB_I = TypeDefinition.Sort.describe(Instant.class);
63
64     private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, DataObjectCodecContext.class,
65         DistinctNodeContainer.class, Instant.class);
66     private static final MethodType NOTIFICATION_TYPE = MethodType.methodType(BaseNotification.class,
67         NotificationCodecContext.class, ContainerNode.class, Instant.class);
68     private static final String INSTANT_FIELD = "instant";
69
70     private final MethodHandle eventProxy;
71
72     NotificationCodecContext(final Class<?> key, final NotificationRuntimeType schema,
73             final CodecContextFactory factory) {
74         super(DataContainerCodecPrototype.from(key, schema, factory));
75         final Class<D> bindingClass = getBindingClass();
76
77         final Class<?> awareClass = factory().getLoader().generateClass(bindingClass, "eventInstantAware",
78             (loader, fqcn, bindingInterface) -> {
79                 final Class<?> codecImpl = loader.getGeneratedClass(bindingClass, "codecImpl");
80
81                 return GeneratorResult.of(new ByteBuddy()
82                     .subclass(codecImpl, ConstructorStrategy.Default.NO_CONSTRUCTORS)
83                     .implement(EVENT_INSTANT_AWARE)
84                     .name(fqcn)
85                     .defineField(INSTANT_FIELD, BB_I, Opcodes.ACC_PRIVATE | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC)
86                     .defineConstructor(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
87                         .withParameters(BB_DOCC, BB_DNC, BB_I)
88                         .intercept(ConstructorImplementation.INSTANCE)
89                     .defineMethod(EVENT_INSTANT_NAME, EVENT_INSTANT_RETTYPE, Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC)
90                         .intercept(EventInstantImplementation.INSTANCE)
91                     .make());
92             });
93
94         final MethodHandle ctor;
95         try {
96             ctor = MethodHandles.publicLookup().findConstructor(awareClass, CONSTRUCTOR_TYPE);
97         } catch (IllegalAccessException | NoSuchMethodException e) {
98             throw new LinkageError("Failed to acquire constructor", e);
99         }
100         eventProxy = ctor.asType(NOTIFICATION_TYPE);
101     }
102
103     @Override
104     public D deserialize(final NormalizedNode data) {
105         checkState(data instanceof ContainerNode, "Unexpected data %s", data);
106         return createBindingProxy((ContainerNode) data);
107     }
108
109     @SuppressWarnings("checkstyle:illegalCatch")
110     @NonNull BaseNotification deserialize(final @NonNull ContainerNode data, final @NonNull Instant eventInstant) {
111         final BaseNotification ret;
112         try {
113             ret = (BaseNotification) eventProxy.invokeExact(this, data, eventInstant);
114         } catch (final Throwable e) {
115             Throwables.throwIfUnchecked(e);
116             throw new LinkageError("Failed to instantiate notification", e);
117         }
118         return verifyNotNull(ret);
119     }
120
121     @Override
122     protected Object deserializeObject(final NormalizedNode normalizedNode) {
123         return deserialize(normalizedNode);
124     }
125
126     private enum ConstructorImplementation implements Implementation {
127         INSTANCE;
128
129         private static final StackManipulation LOAD_INSTANT_ARG = MethodVariableAccess.REFERENCE.loadFrom(3);
130         private static final StackManipulation LOAD_CTOR_ARGS;
131
132         static {
133             try {
134                 LOAD_CTOR_ARGS = MethodVariableAccess.allArgumentsOf(new MethodDescription.ForLoadedConstructor(
135                     AugmentableCodecDataObject.class.getDeclaredConstructor(DataObjectCodecContext.class,
136                         DistinctNodeContainer.class)));
137             } catch (NoSuchMethodException e) {
138                 throw new ExceptionInInitializerError(e);
139             }
140         }
141
142         @Override
143         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
144             return instrumentedType;
145         }
146
147         @Override
148         public ByteCodeAppender appender(final Target implementationTarget) {
149             final TypeDescription instrumentedType = implementationTarget.getInstrumentedType();
150             final InGenericShape superCtor = verifyNotNull(instrumentedType.getSuperClass()).getDeclaredMethods()
151                     .filter(ElementMatchers.isConstructor()).getOnly();
152
153             return new ByteCodeAppender.Simple(
154                 loadThis(),
155                 LOAD_CTOR_ARGS,
156                 MethodInvocation.invoke(superCtor),
157                 loadThis(),
158                 LOAD_INSTANT_ARG,
159                 putField(instrumentedType, INSTANT_FIELD),
160                 MethodReturn.VOID);
161         }
162     }
163
164     private enum EventInstantImplementation implements Implementation {
165         INSTANCE;
166
167         @Override
168         public InstrumentedType prepare(final InstrumentedType instrumentedType) {
169             return instrumentedType;
170         }
171
172         @Override
173         public ByteCodeAppender appender(final Target implementationTarget) {
174             return new ByteCodeAppender.Simple(
175               loadThis(),
176               getField(implementationTarget.getInstrumentedType(), INSTANT_FIELD),
177               MethodReturn.REFERENCE);
178         }
179     }
180 }