Cache NodeContextSuppliers in CodecDataObject
[mdsal.git] / binding / mdsal-binding-dom-codec / src / main / java / org / opendaylight / mdsal / binding / dom / codec / impl / CodecDataObjectCustomizer.java
index 6e90037e5ee9d7e0053839dd48fe5d59836b3793..f9a3b1154082212e66697c8da188410956f6431b 100644 (file)
@@ -7,19 +7,26 @@
  */
 package org.opendaylight.mdsal.binding.dom.codec.impl;
 
+import static com.google.common.base.Verify.verify;
+import static com.google.common.base.Verify.verifyNotNull;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects.ToStringHelper;
+import com.google.common.base.Supplier;
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.util.List;
+import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import javassist.CannotCompileException;
 import javassist.CtClass;
 import javassist.CtField;
 import javassist.CtMethod;
 import javassist.NotFoundException;
+import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader;
 import org.opendaylight.mdsal.binding.dom.codec.loader.CodecClassLoader.Customizer;
@@ -32,23 +39,24 @@ import org.slf4j.LoggerFactory;
  * Private support for generating AbstractDataObject specializations.
  */
 final class CodecDataObjectCustomizer implements Customizer {
-    static final int KEY_OFFSET = -1;
-
     private static final Logger LOG = LoggerFactory.getLogger(CodecDataObjectCustomizer.class);
     private static final CtClass CT_ARFU = StaticClassPool.findClass(AtomicReferenceFieldUpdater.class);
     private static final CtClass CT_BOOLEAN = StaticClassPool.findClass(boolean.class);
+    private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
+    private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
+    private static final CtClass CT_IIC = StaticClassPool.findClass(IdentifiableItemCodec.class);
     private static final CtClass CT_INT = StaticClassPool.findClass(int.class);
+    private static final CtClass CT_NCS = StaticClassPool.findClass(NodeContextSupplier.class);
     private static final CtClass CT_OBJECT = StaticClassPool.findClass(Object.class);
-    private static final CtClass CT_HELPER = StaticClassPool.findClass(ToStringHelper.class);
-    private static final CtClass CT_DATAOBJECT = StaticClassPool.findClass(DataObject.class);
     private static final CtClass[] EMPTY_ARGS = new CtClass[0];
     private static final CtClass[] EQUALS_ARGS = new CtClass[] { CT_DATAOBJECT };
     private static final CtClass[] TOSTRING_ARGS = new CtClass[] { CT_HELPER };
 
-    private final List<Method> properties;
-    private final Method keyMethod;
+    private final ImmutableMap<Method, NodeContextSupplier> properties;
+    private final Entry<Method, IdentifiableItemCodec> keyMethod;
 
-    CodecDataObjectCustomizer(final List<Method> properties, final @Nullable Method keyMethod) {
+    CodecDataObjectCustomizer(final ImmutableMap<Method, NodeContextSupplier> properties,
+            final @Nullable Entry<Method, IdentifiableItemCodec> keyMethod) {
         this.properties = requireNonNull(properties);
         this.keyMethod = keyMethod;
     }
@@ -60,12 +68,11 @@ final class CodecDataObjectCustomizer implements Customizer {
         LOG.trace("Generating class {}", generated.getName());
         generated.addInterface(bindingClass);
 
-        int offset = 0;
-        for (Method method : properties) {
-            generateMethod(loader, generated, method, offset++);
+        for (Method method : properties.keySet()) {
+            generateMethod(loader, generated, method, CT_NCS, "resolve");
         }
         if (keyMethod != null) {
-            generateMethod(loader, generated, keyMethod, KEY_OFFSET);
+            generateMethod(loader, generated, keyMethod.getKey(), CT_IIC, "resolveKey");
         }
 
         // Final bits: codecHashCode() ...
@@ -90,13 +97,52 @@ final class CodecDataObjectCustomizer implements Customizer {
         return ImmutableList.of();
     }
 
+    @Override
+    public Class<?> customizeLoading(final @NonNull Supplier<Class<?>> loader) {
+        final CodecDataObjectCustomizer prev = CodecDataObjectBridge.setup(this);
+        try {
+            final Class<?> result = loader.get();
+
+            /*
+             * This a bit of magic to support NodeContextSupplier constants. These constants need to be resolved while
+             * we have the information needed to find them -- that information is being held in this instance and we
+             * leak it to a thread-local variable held by CodecDataObjectBridge.
+             *
+             * By default the JVM will defer class initialization to first use, which unfortunately is too late for
+             * us, and hence we need to force class to initialize.
+             */
+            try {
+                Class.forName(result.getName(), true, result.getClassLoader());
+            } catch (ClassNotFoundException e) {
+                throw new LinkageError("Failed to find newly-defined " + result, e);
+            }
+
+            return result;
+        } finally {
+            CodecDataObjectBridge.tearDown(prev);
+        }
+    }
+
+
+    @NonNull NodeContextSupplier resolve(final @NonNull String methodName) {
+        final Optional<Entry<Method, NodeContextSupplier>> found = properties.entrySet().stream()
+                .filter(entry -> methodName.equals(entry.getKey().getName())).findAny();
+        verify(found.isPresent(), "Failed to find property for %s in %s", methodName, this);
+        return verifyNotNull(found.get().getValue());
+    }
+
+    @NonNull IdentifiableItemCodec resolveKey(final @NonNull String methodName) {
+        return verifyNotNull(verifyNotNull(keyMethod, "No key method attached for %s in %s", methodName, this)
+            .getValue());
+    }
+
     private String codecHashCodeBody() {
         final StringBuilder sb = new StringBuilder()
                 .append("{\n")
                 .append("final int prime = 31;\n")
                 .append("int result = 1;\n");
 
-        for (Method method : properties) {
+        for (Method method : properties.keySet()) {
             sb.append("result = prime * result + java.util.").append(utilClass(method)).append(".hashCode(")
             .append(method.getName()).append("());\n");
         }
@@ -111,7 +157,7 @@ final class CodecDataObjectCustomizer implements Customizer {
                 .append("final ").append(ifaceName).append(" other = $1;")
                 .append("return true");
 
-        for (Method method : properties) {
+        for (Method method : properties.keySet()) {
             final String methodName = method.getName();
             sb.append("\n&& java.util.").append(utilClass(method)).append(".equals(").append(methodName)
             .append("(), other.").append(methodName).append("())");
@@ -125,7 +171,7 @@ final class CodecDataObjectCustomizer implements Customizer {
         final StringBuilder sb = new StringBuilder()
                 .append("{\n")
                 .append("return $1");
-        for (Method method : properties) {
+        for (Method method : properties.keySet()) {
             final String methodName = method.getName();
             sb.append("\n.add(\"").append(methodName).append("\", ").append(methodName).append("())");
         }
@@ -135,12 +181,19 @@ final class CodecDataObjectCustomizer implements Customizer {
     }
 
     private static void generateMethod(final CodecClassLoader loader, final CtClass generated, final Method method,
-            final int offset) throws CannotCompileException, NotFoundException {
+            final CtClass contextType, final String resolveMethod) throws CannotCompileException, NotFoundException {
         LOG.trace("Generating for method {}", method);
         final String methodName = method.getName();
         final String methodArfu = methodName + "$$$ARFU";
+        final String methodNcs = methodName + "$$$NCS";
+
+        // NodeContextSupplier ...
+        final CtField ncsField = new CtField(contextType, methodNcs, generated);
+        ncsField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
+        generated.addField(ncsField, new StringBuilder().append(CodecDataObjectBridge.class.getName())
+            .append('.').append(resolveMethod).append("(\"").append(methodName).append("\")").toString());
 
-        // AtomicReferenceFieldUpdater ...
+        // ... AtomicReferenceFieldUpdater ...
         final CtField arfuField = new CtField(CT_ARFU, methodArfu, generated);
         arfuField.setModifiers(Modifier.PRIVATE | Modifier.STATIC | Modifier.FINAL);
         generated.addField(arfuField, new StringBuilder().append(CT_ARFU.getName()).append(".newUpdater(")
@@ -159,8 +212,8 @@ final class CodecDataObjectCustomizer implements Customizer {
 
         getter.setBody(new StringBuilder()
             .append("{\n")
-            .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", ").append(offset)
-                .append(");\n")
+            .append("return (").append(retName).append(") codecMember(").append(methodArfu).append(", ")
+                .append(methodNcs).append(");\n")
             .append('}').toString());
         getter.setModifiers(Modifier.PUBLIC | Modifier.FINAL);
         generated.addMethod(getter);