Cleaned up Java Binding code from YANG Tools
[mdsal.git] / binding / mdsal-binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / util / JavassistUtils.java
diff --git a/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/JavassistUtils.java b/binding/mdsal-binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/util/JavassistUtils.java
new file mode 100644 (file)
index 0000000..e3a56e5
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2014 Cisco Systems, Inc. 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.yangtools.sal.binding.generator.util;
+
+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.
+ */
+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;
+    }
+
+    public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
+            final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
+        final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
+        final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
+
+        final CtMethod method = _ctMethod;
+        function1.process(method);
+        it.addMethod(method);
+    }
+
+    public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
+            final Collection<? extends Class<?>> parameters, final MethodGenerator function1) throws CannotCompileException {
+        final CtClass[] pa = new CtClass[parameters.size()];
+
+        int i = 0;
+        for (Class<? extends Object> parameter : parameters) {
+            pa[i] = asCtClass(parameter);
+            ++i;
+        }
+
+        final CtMethod method = new CtMethod(asCtClass(returnType), name, pa, it);
+        function1.process(method);
+        it.addMethod(method);
+    }
+
+    public void staticMethod(final CtClass it, final Class<? extends Object> returnType, final String name,
+            final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
+        final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
+        final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
+        final CtMethod method = _ctMethod;
+        function1.process(method);
+        it.addMethod(method);
+    }
+
+    public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator function1) throws CannotCompileException {
+        for (CtMethod method : source.getMethods()) {
+            if (method.getDeclaringClass() == source) {
+                CtMethod redeclaredMethod = new CtMethod(method, target, null);
+                function1.process(redeclaredMethod);
+                target.addMethod(redeclaredMethod);
+            }
+        }
+    }
+
+    public CtClass createClass(final String fqn, final ClassGenerator cls) throws CannotCompileException {
+        CtClass target = classPool.makeClass(fqn);
+        cls.process(target);
+        return target;
+    }
+
+    public CtClass createClass(final String fqn, final CtClass superInterface, final ClassGenerator cls) throws CannotCompileException {
+        CtClass target = classPool.makeClass(fqn);
+        implementsType(target, superInterface);
+        cls.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 fqn 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
+     */
+    @Beta
+    public synchronized CtClass instantiatePrototype(final String prototype, final String fqn, final ClassCustomizer customizer) throws NotFoundException {
+        final CtClass result = classPool.getAndRename(prototype, fqn);
+        try {
+            customizer.customizeClass(result);
+        } catch (Exception e) {
+            LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
+            result.detach();
+            throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn), e);
+        }
+
+        result.stopPruning(false);
+        return result;
+    }
+
+    public void implementsType(final CtClass it, final CtClass supertype) {
+        Preconditions.checkArgument(supertype.isInterface(), "Supertype must be interface");
+        it.addInterface(supertype);
+    }
+
+    public CtClass asCtClass(final Class<? extends Object> class1) {
+        return get(this.classPool, class1);
+    }
+
+    public CtField field(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
+        final CtField field = new CtField(asCtClass(returnValue), name, it);
+        field.setModifiers(Modifier.PUBLIC);
+        it.addField(field);
+        return field;
+    }
+
+    public CtField staticField(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
+        return staticField(it, name, returnValue, null);
+    }
+
+    public CtField staticField(final CtClass it, final String name,
+            final Class<? extends Object> returnValue,
+            final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
+        final CtField field = new CtField(asCtClass(returnValue), name, it);
+        field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
+        it.addField(field);
+
+        if (sourceGenerator != null) {
+            sourceGenerator.appendField(field, null);
+        }
+
+        return field;
+    }
+
+    public CtClass get(final ClassPool pool, final Class<? extends Object> cls) {
+        try {
+            return pool.get(cls.getName());
+        } catch (NotFoundException nfe1) {
+            appendClassLoaderIfMissing(cls.getClassLoader());
+            try {
+                return pool.get(cls.getName());
+            } catch (final NotFoundException nfe2) {
+                LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
+                pool.appendClassPath(new ClassClassPath(cls));
+                try {
+                    return pool.get(cls.getName());
+                } catch (NotFoundException e) {
+                    LOG.warn("Failed to load class {} from pool {}", cls, pool, e);
+                    throw new IllegalStateException("Failed to load class", e);
+                }
+            }
+        }
+    }
+
+    public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
+        if (!loaderClassPaths.containsKey(loader)) {
+            final ClassPath ctLoader = new LoaderClassPath(loader);
+            classPool.appendClassPath(ctLoader);
+            loaderClassPaths.put(loader, ctLoader);
+        }
+    }
+
+    public void ensureClassLoader(final Class<?> child) {
+        appendClassLoaderIfMissing(child.getClassLoader());
+    }
+}