Correct docs declaration
[mdsal.git] / binding2 / mdsal-binding2-runtime / src / main / java / org / opendaylight / mdsal / binding / javav2 / runtime / javassist / JavassistUtils.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies s.r.o. 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.javav2.runtime.javassist;
9
10 import static java.util.Objects.requireNonNull;
11
12 import com.google.common.annotations.Beta;
13 import com.google.common.base.Preconditions;
14 import java.util.Collection;
15 import java.util.Map;
16 import java.util.WeakHashMap;
17 import javassist.CannotCompileException;
18 import javassist.ClassClassPath;
19 import javassist.ClassPath;
20 import javassist.ClassPool;
21 import javassist.CtClass;
22 import javassist.CtField;
23 import javassist.CtMethod;
24 import javassist.LoaderClassPath;
25 import javassist.Modifier;
26 import javassist.NotFoundException;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Users of this utility class are expected to synchronize on this instance it
32  * they need to ensure atomic operations on it.
33  */
34 @Beta
35 public final class JavassistUtils {
36
37     private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
38
39     private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
40     private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
41     private final ClassPool classPool;
42
43     private JavassistUtils(final ClassPool pool) {
44         classPool = requireNonNull(pool);
45     }
46
47     /**
48      * Get a utility instance for a particular class pool. A new instance is
49      * created if this is a new pool. If an instance already exists, is is
50      * returned.
51      *
52      * @param pool
53      *            - backing class pool
54      * @return shared utility instance for specified pool
55      */
56     public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
57         JavassistUtils ret = INSTANCES.get(requireNonNull(pool));
58         if (ret == null) {
59             ret = new JavassistUtils(pool);
60             INSTANCES.put(pool, ret);
61         }
62         return ret;
63     }
64
65     /**
66      * Generate and add method to class.
67      *
68      * @param baseClass
69      *            - class for adding method
70      * @param methodReturnType
71      *            - return type of method
72      * @param methodName
73      *            - name of method
74      * @param methodParameter
75      *            - parameter of method
76      * @param methodGenerator
77      *            - method generator
78      * @throws CannotCompileException
79      */
80     public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
81             final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
82         final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
83         final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
84
85         methodGenerator.process(method);
86         baseClass.addMethod(method);
87     }
88
89     /**
90      * Generate and add method to class.
91      *
92      * @param baseClass
93      *            - class for adding method
94      * @param methodReturnType
95      *            - return type of method
96      * @param methodName
97      *            - name of method
98      * @param methodParameters
99      *            - parameters of method
100      * @param methodGenerator
101      *            - method generator
102      * @throws CannotCompileException
103      */
104     public void method(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
105             final Collection<? extends Class<?>> methodParameters, final MethodGenerator methodGenerator)
106             throws CannotCompileException {
107         final CtClass[] pa = new CtClass[methodParameters.size()];
108
109         int i = 0;
110         for (final Class<?> parameter : methodParameters) {
111             pa[i] = asCtClass(parameter);
112             ++i;
113         }
114
115         final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
116         methodGenerator.process(method);
117         baseClass.addMethod(method);
118     }
119
120     /**
121      * Generate and add static method to class.
122      *
123      * @param baseClass
124      *            - class for adding method
125      * @param methodReturnType
126      *            - return type of method
127      * @param methodName
128      *            - name of method
129      * @param methodParameter
130      *            - parameter of method
131      * @param methodGenerator
132      *            - method generator
133      * @throws CannotCompileException
134      */
135     public void staticMethod(final CtClass baseClass, final Class<?> methodReturnType, final String methodName,
136             final Class<?> methodParameter, final MethodGenerator methodGenerator) throws CannotCompileException {
137         final CtClass[] pa = new CtClass[] { asCtClass(methodParameter) };
138         final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
139         methodGenerator.process(method);
140         baseClass.addMethod(method);
141     }
142
143     /**
144      * Implement methods to class from other class.
145      *
146      * @param target
147      *            - class for implementing methods
148      * @param source
149      *            - source class of methods to be implemented in target
150      * @param methodGenerator
151      *            - method generator
152      * @throws CannotCompileException
153      */
154     public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator methodGenerator)
155             throws CannotCompileException {
156         for (final CtMethod method : source.getMethods()) {
157             if (method.getDeclaringClass() == source) {
158                 final CtMethod redeclaredMethod = new CtMethod(method, target, null);
159                 methodGenerator.process(redeclaredMethod);
160                 target.addMethod(redeclaredMethod);
161             }
162         }
163     }
164
165     /**
166      * Generate and add class to global class pool.
167      *
168      * @param className
169      *            - name of class
170      * @param classGenerator
171      *            - class generator
172      * @return generated class
173      * @throws CannotCompileException
174      */
175     public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException {
176         final CtClass target = classPool.makeClass(className);
177         classGenerator.process(target);
178         return target;
179     }
180
181     /**
182      * Generate and add class to global class pool with implemented interface.
183      *
184      * @param className
185      *            - name of class
186      * @param superInterface
187      *            - interface to be implemented to the class
188      * @param classGenerator
189      *            - class generator
190      * @return generated class with interface
191      * @throws CannotCompileException
192      */
193     public CtClass createClass(final String className, final CtClass superInterface, final ClassGenerator classGenerator)
194             throws CannotCompileException {
195         final CtClass target = classPool.makeClass(className);
196         implementsType(target, superInterface);
197         classGenerator.process(target);
198         return target;
199     }
200
201     /**
202      * Instantiate a new class based on a prototype. The class is set to
203      * automatically prune.
204      *
205      * @param prototype
206      *            - prototype class fully qualified name
207      * @param target
208      *            - target class fully qualified name
209      * @param customizer
210      *            - customization callback to be invoked on the new class
211      * @return An instance of the new class
212      * @throws NotFoundException
213      *             - when the prototype class is not found
214      */
215     public synchronized CtClass instantiatePrototype(final String prototype, final String target,
216             final ClassCustomizer customizer) throws NotFoundException {
217         final CtClass result = classPool.getAndRename(prototype, target);
218
219         try {
220             customizer.customizeClass(result);
221         } catch (final Exception e) {
222             LOG.warn("Failed to customize {} from prototype {}", target, prototype, e);
223             result.detach();
224             throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target),
225                     e);
226         }
227
228         result.stopPruning(false);
229         return result;
230     }
231
232     /**
233      * Implements type to class.
234      *
235      * @param baseClass
236      *            - class for implements interface
237      * @param superInterface
238      *            - interface to be implemented
239      */
240     public void implementsType(final CtClass baseClass, final CtClass superInterface) {
241         Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface");
242         baseClass.addInterface(superInterface);
243     }
244
245     /**
246      * Get class from class pool.
247      *
248      * @param class1
249      *            - class for getting from class pool
250      * @return class
251      */
252     public CtClass asCtClass(final Class<?> class1) {
253         return get(this.classPool, class1);
254     }
255
256     /**
257      * Create and add field to class.
258      *
259      * @param baseClass
260      *            - class for adding field
261      * @param fieldName
262      *            - name of field
263      * @param fieldType
264      *            - type of field
265      * @return class of field
266      * @throws CannotCompileException
267      */
268     public CtField field(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
269             throws CannotCompileException {
270         final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
271         field.setModifiers(Modifier.PUBLIC);
272         baseClass.addField(field);
273         return field;
274     }
275
276     /**
277      * Create and add static field to class.
278      *
279      * @param baseClass
280      *            - class for adding field
281      * @param fieldName
282      *            - name of field
283      * @param fieldType
284      *            - type of field
285      * @return class of field
286      * @throws CannotCompileException
287      */
288     public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
289             throws CannotCompileException {
290         return staticField(baseClass, fieldName, fieldType, null);
291     }
292
293     /**
294      * Create and add static field to class.
295      *
296      * @param baseClass
297      *            - class for adding field
298      * @param fieldName
299      *            - name of field
300      * @param fieldType
301      *            - type of field
302      * @param sourceGenerator
303      *            - source generator
304      * @return class of field
305      * @throws CannotCompileException
306      */
307     public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType,
308             final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
309         final CtField field = new CtField(asCtClass(fieldType), fieldName, baseClass);
310         field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
311         baseClass.addField(field);
312
313         if (sourceGenerator != null) {
314             sourceGenerator.appendField(field, null);
315         }
316
317         return field;
318     }
319
320     /**
321      * Get class from pool.
322      *
323      * @param pool
324      *            - class pool
325      * @param clazz
326      *            - search class in class pool
327      * @return class if exists
328      */
329     public CtClass get(final ClassPool pool, final Class<? extends Object> clazz) {
330         try {
331             return pool.get(clazz.getName());
332         } catch (final NotFoundException nfe1) {
333             appendClassLoaderIfMissing(clazz.getClassLoader());
334             try {
335                 return pool.get(clazz.getName());
336             } catch (final NotFoundException nfe2) {
337                 LOG.warn("Appending ClassClassPath for {}", clazz, nfe2);
338                 pool.appendClassPath(new ClassClassPath(clazz));
339                 try {
340                     return pool.get(clazz.getName());
341                 } catch (final NotFoundException e) {
342                     LOG.warn("Failed to load class {} from pool {}", clazz, pool, e);
343                     throw new IllegalStateException("Failed to load class", e);
344                 }
345             }
346         }
347     }
348
349     /**
350      * Append class to class pool if doesn't exist.
351      *
352      * @param loader
353      *            - class loader of search class
354      */
355     public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
356         if (!loaderClassPaths.containsKey(loader)) {
357             final ClassPath ctLoader = new LoaderClassPath(loader);
358             classPool.appendClassPath(ctLoader);
359             loaderClassPaths.put(loader, ctLoader);
360         }
361     }
362
363     /**
364      * Ensure if is class in class loader.
365      *
366      * @param child
367      *            - search class
368      */
369     public void ensureClassLoader(final Class<?> child) {
370         appendClassLoaderIfMissing(child.getClassLoader());
371     }
372 }