Binding v2 DOM Codec - Javassist part 26/58326/6
authorJakub Toth <jakub.toth@pantheon.tech>
Tue, 6 Jun 2017 08:33:16 +0000 (10:33 +0200)
committerJakub Toth <jakub.toth@pantheon.tech>
Wed, 7 Jun 2017 12:28:35 +0000 (14:28 +0200)
Change-Id: I9f07629ef79729650e44b1caba239a25a23db81e
Signed-off-by: Jakub Toth <jakub.toth@pantheon.tech>
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java [new file with mode: 0644]
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java [new file with mode: 0644]
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java [new file with mode: 0644]
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java [new file with mode: 0644]
binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java [new file with mode: 0644]

diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassCustomizer.java
new file mode 100644 (file)
index 0000000..5b94d28
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
+
+import com.google.common.annotations.Beta;
+import javassist.CtClass;
+
+/**
+ * Interface allowing customization of classes after loading.
+ */
+@Beta
+public interface ClassCustomizer {
+
+    /**
+     * Customize a class.
+     *
+     * @param cls
+     *            - class to be customized
+     * @throws Exception
+     *             - when a problem ensues
+     */
+    void customizeClass(CtClass cls) throws Exception;
+}
\ No newline at end of file
diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/ClassGenerator.java
new file mode 100644 (file)
index 0000000..d50ad04
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
+
+import com.google.common.annotations.Beta;
+import javassist.CannotCompileException;
+import javassist.CtClass;
+
+/**
+ * Interface allowing generate new classes.
+ */
+@Beta
+public interface ClassGenerator {
+
+    /**
+     * Process class.
+     *
+     * @param baseClass
+     *            - class to be generated
+     * @throws CannotCompileException
+     */
+    void process(CtClass baseClass) throws CannotCompileException;
+}
diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/JavassistUtils.java
new file mode 100644 (file)
index 0000000..291b20c
--- /dev/null
@@ -0,0 +1,370 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import java.util.Collection;
+import java.util.Map;
+import java.util.WeakHashMap;
+import javassist.CannotCompileException;
+import javassist.ClassClassPath;
+import javassist.ClassPath;
+import javassist.ClassPool;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+import javassist.LoaderClassPath;
+import javassist.Modifier;
+import javassist.NotFoundException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Users of this utility class are expected to synchronize on this instance it
+ * they need to ensure atomic operations on it.
+ */
+@Beta
+public final class JavassistUtils {
+
+    private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
+
+    private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
+    private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
+    private final ClassPool classPool;
+
+    private JavassistUtils(final ClassPool pool) {
+        classPool = Preconditions.checkNotNull(pool);
+    }
+
+    /**
+     * Get a utility instance for a particular class pool. A new instance is
+     * created if this is a new pool. If an instance already exists, is is
+     * returned.
+     *
+     * @param pool
+     *            - backing class pool
+     * @return shared utility instance for specified pool
+     */
+    public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
+        JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
+        if (ret == null) {
+            ret = new JavassistUtils(pool);
+            INSTANCES.put(pool, ret);
+        }
+        return ret;
+    }
+
+    /**
+     * Generate and add method to class.
+     *
+     * @param baseClass
+     *            - class for adding method
+     * @param methodReturnType
+     *            - return type of method
+     * @param methodName
+     *            - name of method
+     * @param methodParameter
+     *            - parameter of method
+     * @param methodGenerator
+     *            - method generator
+     * @throws CannotCompileException
+     */
+    public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
+            final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
+        final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
+        final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
+
+        methodGenerator.process(method);
+        baseClass.addMethod(method);
+    }
+
+    /**
+     * Generate and add method to class.
+     *
+     * @param baseClass
+     *            - class for adding method
+     * @param methodReturnType
+     *            - return type of method
+     * @param methodName
+     *            - name of method
+     * @param methodParameters
+     *            - parameters of method
+     * @param methodGenerator
+     *            - method generator
+     * @throws CannotCompileException
+     */
+    public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
+            final Collection<? extends Class<?>> methodParameters, final MethodGenerator methodGenerator)
+            throws CannotCompileException {
+        final CtClass[] pa = new CtClass[methodParameters.size()];
+
+        int i = 0;
+        for (final Class<?> parameter : methodParameters) {
+            pa[i] = asCtClass(parameter);
+            ++i;
+        }
+
+        final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
+        methodGenerator.process(method);
+        baseClass.addMethod(method);
+    }
+
+    /**
+     * Generate and add static method to class.
+     *
+     * @param baseClass
+     *            - class for adding method
+     * @param methodReturnType
+     *            - return type of method
+     * @param methodName
+     *            - name of method
+     * @param methodParameter
+     *            - parameter of method
+     * @param methodGenerator
+     *            - method generator
+     * @throws CannotCompileException
+     */
+    public void staticMethod(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
+            final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
+        final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
+        final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
+        methodGenerator.process(method);
+        baseClass.addMethod(method);
+    }
+
+    /**
+     * Implement methods to class from other class.
+     *
+     * @param target
+     *            - class for implementing methods
+     * @param source
+     *            - source class of methods to be implemented in target
+     * @param methodGenerator
+     *            - method generator
+     * @throws CannotCompileException
+     */
+    public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator methodGenerator)
+            throws CannotCompileException {
+        for (final CtMethod method : source.getMethods()) {
+            if (method.getDeclaringClass() == source) {
+                final CtMethod redeclaredMethod = new CtMethod(method, target, null);
+                methodGenerator.process(redeclaredMethod);
+                target.addMethod(redeclaredMethod);
+            }
+        }
+    }
+
+    /**
+     * Generate and add class to global class pool.
+     *
+     * @param className
+     *            - name of class
+     * @param classGenerator
+     *            - class generator
+     * @return generated class
+     * @throws CannotCompileException
+     */
+    public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException {
+        final CtClass target = classPool.makeClass(className);
+        classGenerator.process(target);
+        return target;
+    }
+
+    /**
+     * Generate and add class to global class pool with implemented interface.
+     *
+     * @param className
+     *            - name of class
+     * @param superInterface
+     *            - interface to be implemented to the class
+     * @param classGenerator
+     *            - class generator
+     * @return generated class with interface
+     * @throws CannotCompileException
+     */
+    public CtClass createClass(final String className, final CtClass superInterface, final ClassGenerator classGenerator)
+            throws CannotCompileException {
+        final CtClass target = classPool.makeClass(className);
+        implementsType(target, superInterface);
+        classGenerator.process(target);
+        return target;
+    }
+
+    /**
+     * Instantiate a new class based on a prototype. The class is set to
+     * automatically prune.
+     *
+     * @param prototype
+     *            - prototype class fully qualified name
+     * @param target
+     *            - target class fully qualified name
+     * @param customizer
+     *            - customization callback to be invoked on the new class
+     * @return An instance of the new class
+     * @throws NotFoundException
+     *             - when the prototype class is not found
+     */
+    public synchronized CtClass instantiatePrototype(final String prototype, final String target,
+            final ClassCustomizer customizer) throws NotFoundException {
+        final CtClass result = classPool.getAndRename(prototype, target);
+
+        try {
+            customizer.customizeClass(result);
+        } catch (final Exception e) {
+            LOG.warn("Failed to customize {} from prototype {}", target, prototype, e);
+            result.detach();
+            throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target),
+                    e);
+        }
+
+        result.stopPruning(false);
+        return result;
+    }
+
+    /**
+     * Implements type to class.
+     *
+     * @param baseClass
+     *            - class for implements interface
+     * @param superInterface
+     *            - interface to be implemented
+     */
+    public void implementsType(final CtClass baseClass, final CtClass superInterface) {
+        Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface");
+        baseClass.addInterface(superInterface);
+    }
+
+    /**
+     * Get class from class pool.
+     *
+     * @param class1
+     *            - class for getting from class pool
+     * @return class
+     */
+    public CtClass asCtClass(final Class<?> class1) {
+        return get(this.classPool, class1);
+    }
+
+    /**
+     * Create and add field to class.
+     *
+     * @param baseClass
+     *            - class for adding field
+     * @param fieldName
+     *            - name of field
+     * @param fieldType
+     *            - type of field
+     * @return class of field
+     * @throws CannotCompileException
+     */
+    public CtField field(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
+            throws CannotCompileException {
+        final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
+        field.setModifiers(Modifier.PUBLIC);
+        baseClass.addField(field);
+        return field;
+    }
+
+    /**
+     * Create and add static field to class.
+     *
+     * @param baseClass
+     *            - class for adding field
+     * @param fieldName
+     *            - name of field
+     * @param fieldType
+     *            - type of field
+     * @return class of field
+     * @throws CannotCompileException
+     */
+    public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
+            throws CannotCompileException {
+        return staticField(baseClass, fieldName, fieldType, null);
+    }
+
+    /**
+     * Create and add static field to class.
+     *
+     * @param baseClass
+     *            - class for adding field
+     * @param fieldName
+     *            - name of field
+     * @param fieldType
+     *            - type of field
+     * @param sourceGenerator
+     *            - source generator
+     * @return class of field
+     * @throws CannotCompileException
+     */
+    public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType,
+            final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
+        final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
+        field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
+        baseClass.addField(field);
+
+        if (sourceGenerator != null) {
+            sourceGenerator.appendField(field, null);
+        }
+
+        return field;
+    }
+
+    /**
+     * Get class from pool.
+     *
+     * @param pool
+     *            - class pool
+     * @param clazz
+     *            - search class in class pool
+     * @return class if exists
+     */
+    public CtClass get(final ClassPool pool, final Class<? extends Object> clazz) {
+        try {
+            return pool.get(clazz.getName());
+        } catch (final NotFoundException nfe1) {
+            appendClassLoaderIfMissing(clazz.getClassLoader());
+            try {
+                return pool.get(clazz.getName());
+            } catch (final NotFoundException nfe2) {
+                LOG.warn("Appending ClassClassPath for {}", clazz, nfe2);
+                pool.appendClassPath(new ClassClassPath(clazz));
+                try {
+                    return pool.get(clazz.getName());
+                } catch (final NotFoundException e) {
+                    LOG.warn("Failed to load class {} from pool {}", clazz, pool, e);
+                    throw new IllegalStateException("Failed to load class", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Append class to class pool if doesn't exist.
+     *
+     * @param loader
+     *            - class loader of search class
+     */
+    public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
+        if (!loaderClassPaths.containsKey(loader)) {
+            final ClassPath ctLoader = new LoaderClassPath(loader);
+            classPool.appendClassPath(ctLoader);
+            loaderClassPaths.put(loader, ctLoader);
+        }
+    }
+
+    /**
+     * Ensure if is class in class loader.
+     *
+     * @param child
+     *            - search class
+     */
+    public void ensureClassLoader(final Class<?> child) {
+        appendClassLoaderIfMissing(child.getClassLoader());
+    }
+}
diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/MethodGenerator.java
new file mode 100644 (file)
index 0000000..cb911be
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
+
+import com.google.common.annotations.Beta;
+import javassist.CannotCompileException;
+import javassist.CtMethod;
+
+/**
+ * Interface allowing generator new methods.
+ */
+@Beta
+public interface MethodGenerator {
+
+    /**
+     * Process method.
+     *
+     * @param method
+     *            - method to be generated
+     * @throws CannotCompileException
+     */
+    void process(CtMethod method) throws CannotCompileException;
+}
diff --git a/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java b/binding2/mdsal-binding2-generator-impl/src/main/java/org/opendaylight/mdsal/binding/javav2/generator/impl/util/javassist/SourceCodeGenerator.java
new file mode 100644 (file)
index 0000000..ec31c88
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2017 Pantheon Technologies s.r.o. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
+
+import com.google.common.annotations.Beta;
+import javassist.CtClass;
+import javassist.CtField;
+import javassist.CtMethod;
+
+/**
+ * Interface for a class that that generates readable source code for a runtime generated class.
+ * The appendField/appendMethod methods append source code to a temporary output. When outputGeneratedSource
+ * is called, the entire class source code is generated and outputted.
+ */
+@Beta
+public interface SourceCodeGenerator {
+
+    /**
+     * Appends the given class field and value to the temporary output.
+     */
+    void appendField(CtField field, String value);
+
+    /**
+     * Appends the given method and source code body to the temporary output.
+     */
+    void appendMethod(CtMethod method, String code);
+
+    /**
+     * Generates the full source code for the given class and outputs it.
+     */
+    void outputGeneratedSource(CtClass ctClass);
+}