2 * Copyright (c) 2017 Pantheon Technologies s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.mdsal.binding.javav2.generator.impl.util.javassist;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
14 import java.util.WeakHashMap;
15 import javassist.CannotCompileException;
16 import javassist.ClassClassPath;
17 import javassist.ClassPath;
18 import javassist.ClassPool;
19 import javassist.CtClass;
20 import javassist.CtField;
21 import javassist.CtMethod;
22 import javassist.LoaderClassPath;
23 import javassist.Modifier;
24 import javassist.NotFoundException;
25 import org.slf4j.Logger;
26 import org.slf4j.LoggerFactory;
29 * Users of this utility class are expected to synchronize on this instance it
30 * they need to ensure atomic operations on it.
33 public final class JavassistUtils {
35 private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
37 private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
38 private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
39 private final ClassPool classPool;
41 private JavassistUtils(final ClassPool pool) {
42 classPool = Preconditions.checkNotNull(pool);
46 * Get a utility instance for a particular class pool. A new instance is
47 * created if this is a new pool. If an instance already exists, is is
51 * - backing class pool
52 * @return shared utility instance for specified pool
54 public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
55 JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
57 ret = new JavassistUtils(pool);
58 INSTANCES.put(pool, ret);
64 * Generate and add method to class.
67 * - class for adding method
68 * @param methodReturnType
69 * - return type of method
72 * @param methodParameter
73 * - parameter of method
74 * @param methodGenerator
76 * @throws CannotCompileException
78 public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
79 final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
80 final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
81 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
83 methodGenerator.process(method);
84 baseClass.addMethod(method);
88 * Generate and add method to class.
91 * - class for adding method
92 * @param methodReturnType
93 * - return type of method
96 * @param methodParameters
97 * - parameters of method
98 * @param methodGenerator
100 * @throws CannotCompileException
102 public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
103 final Collection<? extends Class<?>> methodParameters, final MethodGenerator methodGenerator)
104 throws CannotCompileException {
105 final CtClass[] pa = new CtClass[methodParameters.size()];
108 for (final Class<?> parameter : methodParameters) {
109 pa[i] = asCtClass(parameter);
113 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
114 methodGenerator.process(method);
115 baseClass.addMethod(method);
119 * Generate and add static method to class.
122 * - class for adding method
123 * @param methodReturnType
124 * - return type of method
127 * @param methodParameter
128 * - parameter of method
129 * @param methodGenerator
131 * @throws CannotCompileException
133 public void staticMethod(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
134 final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
135 final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
136 final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
137 methodGenerator.process(method);
138 baseClass.addMethod(method);
142 * Implement methods to class from other class.
145 * - class for implementing methods
147 * - source class of methods to be implemented in target
148 * @param methodGenerator
150 * @throws CannotCompileException
152 public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator methodGenerator)
153 throws CannotCompileException {
154 for (final CtMethod method : source.getMethods()) {
155 if (method.getDeclaringClass() == source) {
156 final CtMethod redeclaredMethod = new CtMethod(method, target, null);
157 methodGenerator.process(redeclaredMethod);
158 target.addMethod(redeclaredMethod);
164 * Generate and add class to global class pool.
168 * @param classGenerator
170 * @return generated class
171 * @throws CannotCompileException
173 public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException {
174 final CtClass target = classPool.makeClass(className);
175 classGenerator.process(target);
180 * Generate and add class to global class pool with implemented interface.
184 * @param superInterface
185 * - interface to be implemented to the class
186 * @param classGenerator
188 * @return generated class with interface
189 * @throws CannotCompileException
191 public CtClass createClass(final String className, final CtClass superInterface, final ClassGenerator classGenerator)
192 throws CannotCompileException {
193 final CtClass target = classPool.makeClass(className);
194 implementsType(target, superInterface);
195 classGenerator.process(target);
200 * Instantiate a new class based on a prototype. The class is set to
201 * automatically prune.
204 * - prototype class fully qualified name
206 * - target class fully qualified name
208 * - customization callback to be invoked on the new class
209 * @return An instance of the new class
210 * @throws NotFoundException
211 * - when the prototype class is not found
213 public synchronized CtClass instantiatePrototype(final String prototype, final String target,
214 final ClassCustomizer customizer) throws NotFoundException {
215 final CtClass result = classPool.getAndRename(prototype, target);
218 customizer.customizeClass(result);
219 } catch (final Exception e) {
220 LOG.warn("Failed to customize {} from prototype {}", target, prototype, e);
222 throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target),
226 result.stopPruning(false);
231 * Implements type to class.
234 * - class for implements interface
235 * @param superInterface
236 * - interface to be implemented
238 public void implementsType(final CtClass baseClass, final CtClass superInterface) {
239 Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface");
240 baseClass.addInterface(superInterface);
244 * Get class from class pool.
247 * - class for getting from class pool
250 public CtClass asCtClass(final Class<?> class1) {
251 return get(this.classPool, class1);
255 * Create and add field to class.
258 * - class for adding field
263 * @return class of field
264 * @throws CannotCompileException
266 public CtField field(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
267 throws CannotCompileException {
268 final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
269 field.setModifiers(Modifier.PUBLIC);
270 baseClass.addField(field);
275 * Create and add static field to class.
278 * - class for adding field
283 * @return class of field
284 * @throws CannotCompileException
286 public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
287 throws CannotCompileException {
288 return staticField(baseClass, fieldName, fieldType, null);
292 * Create and add static field to class.
295 * - class for adding field
300 * @param sourceGenerator
302 * @return class of field
303 * @throws CannotCompileException
305 public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType,
306 final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
307 final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
308 field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
309 baseClass.addField(field);
311 if (sourceGenerator != null) {
312 sourceGenerator.appendField(field, null);
319 * Get class from pool.
324 * - search class in class pool
325 * @return class if exists
327 public CtClass get(final ClassPool pool, final Class<? extends Object> clazz) {
329 return pool.get(clazz.getName());
330 } catch (final NotFoundException nfe1) {
331 appendClassLoaderIfMissing(clazz.getClassLoader());
333 return pool.get(clazz.getName());
334 } catch (final NotFoundException nfe2) {
335 LOG.warn("Appending ClassClassPath for {}", clazz, nfe2);
336 pool.appendClassPath(new ClassClassPath(clazz));
338 return pool.get(clazz.getName());
339 } catch (final NotFoundException e) {
340 LOG.warn("Failed to load class {} from pool {}", clazz, pool, e);
341 throw new IllegalStateException("Failed to load class", e);
348 * Append class to class pool if doesn't exist.
351 * - class loader of search class
353 public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
354 if (!loaderClassPaths.containsKey(loader)) {
355 final ClassPath ctLoader = new LoaderClassPath(loader);
356 classPool.appendClassPath(ctLoader);
357 loaderClassPaths.put(loader, ctLoader);
362 * Ensure if is class in class loader.
367 public void ensureClassLoader(final Class<?> child) {
368 appendClassLoaderIfMissing(child.getClassLoader());