Binding v2 DOM Codec - Javassist part
[mdsal.git] / binding2 / mdsal-binding2-generator-impl / src / main / java / org / opendaylight / mdsal / binding / javav2 / generator / impl / util / 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.generator.impl.util.javassist;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
13 import java.util.Map;
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;
27
28 /**
29  * Users of this utility class are expected to synchronize on this instance it
30  * they need to ensure atomic operations on it.
31  */
32 @Beta
33 public final class JavassistUtils {
34
35     private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
36
37     private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
38     private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
39     private final ClassPool classPool;
40
41     private JavassistUtils(final ClassPool pool) {
42         classPool = Preconditions.checkNotNull(pool);
43     }
44
45     /**
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
48      * returned.
49      *
50      * @param pool
51      *            - backing class pool
52      * @return shared utility instance for specified pool
53      */
54     public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
55         JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
56         if (ret == null) {
57             ret = new JavassistUtils(pool);
58             INSTANCES.put(pool, ret);
59         }
60         return ret;
61     }
62
63     /**
64      * Generate and add method to class.
65      *
66      * @param baseClass
67      *            - class for adding method
68      * @param methodReturnType
69      *            - return type of method
70      * @param methodName
71      *            - name of method
72      * @param methodParameter
73      *            - parameter of method
74      * @param methodGenerator
75      *            - method generator
76      * @throws CannotCompileException
77      */
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);
82
83         methodGenerator.process(method);
84         baseClass.addMethod(method);
85     }
86
87     /**
88      * Generate and add method to class.
89      *
90      * @param baseClass
91      *            - class for adding method
92      * @param methodReturnType
93      *            - return type of method
94      * @param methodName
95      *            - name of method
96      * @param methodParameters
97      *            - parameters of method
98      * @param methodGenerator
99      *            - method generator
100      * @throws CannotCompileException
101      */
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()];
106
107         int i = 0;
108         for (final Class<?> parameter : methodParameters) {
109             pa[i] = asCtClass(parameter);
110             ++i;
111         }
112
113         final CtMethod method = new CtMethod(asCtClass(methodReturnType), methodName, pa, baseClass);
114         methodGenerator.process(method);
115         baseClass.addMethod(method);
116     }
117
118     /**
119      * Generate and add static method to class.
120      *
121      * @param baseClass
122      *            - class for adding method
123      * @param methodReturnType
124      *            - return type of method
125      * @param methodName
126      *            - name of method
127      * @param methodParameter
128      *            - parameter of method
129      * @param methodGenerator
130      *            - method generator
131      * @throws CannotCompileException
132      */
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);
139     }
140
141     /**
142      * Implement methods to class from other class.
143      *
144      * @param target
145      *            - class for implementing methods
146      * @param source
147      *            - source class of methods to be implemented in target
148      * @param methodGenerator
149      *            - method generator
150      * @throws CannotCompileException
151      */
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);
159             }
160         }
161     }
162
163     /**
164      * Generate and add class to global class pool.
165      *
166      * @param className
167      *            - name of class
168      * @param classGenerator
169      *            - class generator
170      * @return generated class
171      * @throws CannotCompileException
172      */
173     public CtClass createClass(final String className, final ClassGenerator classGenerator) throws CannotCompileException {
174         final CtClass target = classPool.makeClass(className);
175         classGenerator.process(target);
176         return target;
177     }
178
179     /**
180      * Generate and add class to global class pool with implemented interface.
181      *
182      * @param className
183      *            - name of class
184      * @param superInterface
185      *            - interface to be implemented to the class
186      * @param classGenerator
187      *            - class generator
188      * @return generated class with interface
189      * @throws CannotCompileException
190      */
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);
196         return target;
197     }
198
199     /**
200      * Instantiate a new class based on a prototype. The class is set to
201      * automatically prune.
202      *
203      * @param prototype
204      *            - prototype class fully qualified name
205      * @param target
206      *            - target class fully qualified name
207      * @param customizer
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
212      */
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);
216
217         try {
218             customizer.customizeClass(result);
219         } catch (final Exception e) {
220             LOG.warn("Failed to customize {} from prototype {}", target, prototype, e);
221             result.detach();
222             throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, target),
223                     e);
224         }
225
226         result.stopPruning(false);
227         return result;
228     }
229
230     /**
231      * Implements type to class.
232      *
233      * @param baseClass
234      *            - class for implements interface
235      * @param superInterface
236      *            - interface to be implemented
237      */
238     public void implementsType(final CtClass baseClass, final CtClass superInterface) {
239         Preconditions.checkArgument(superInterface.isInterface(), "Supertype must be interface");
240         baseClass.addInterface(superInterface);
241     }
242
243     /**
244      * Get class from class pool.
245      *
246      * @param class1
247      *            - class for getting from class pool
248      * @return class
249      */
250     public CtClass asCtClass(final Class<?> class1) {
251         return get(this.classPool, class1);
252     }
253
254     /**
255      * Create and add field to class.
256      *
257      * @param baseClass
258      *            - class for adding field
259      * @param fieldName
260      *            - name of field
261      * @param fieldType
262      *            - type of field
263      * @return class of field
264      * @throws CannotCompileException
265      */
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);
271         return field;
272     }
273
274     /**
275      * Create and add static field to class.
276      *
277      * @param baseClass
278      *            - class for adding field
279      * @param fieldName
280      *            - name of field
281      * @param fieldType
282      *            - type of field
283      * @return class of field
284      * @throws CannotCompileException
285      */
286     public CtField staticField(final CtClass baseClass, final String fieldName, final Class<?> fieldType)
287             throws CannotCompileException {
288         return staticField(baseClass, fieldName, fieldType, null);
289     }
290
291     /**
292      * Create and add static field to class.
293      *
294      * @param baseClass
295      *            - class for adding field
296      * @param fieldName
297      *            - name of field
298      * @param fieldType
299      *            - type of field
300      * @param sourceGenerator
301      *            - source generator
302      * @return class of field
303      * @throws CannotCompileException
304      */
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);
310
311         if (sourceGenerator != null) {
312             sourceGenerator.appendField(field, null);
313         }
314
315         return field;
316     }
317
318     /**
319      * Get class from pool.
320      *
321      * @param pool
322      *            - class pool
323      * @param clazz
324      *            - search class in class pool
325      * @return class if exists
326      */
327     public CtClass get(final ClassPool pool, final Class<? extends Object> clazz) {
328         try {
329             return pool.get(clazz.getName());
330         } catch (final NotFoundException nfe1) {
331             appendClassLoaderIfMissing(clazz.getClassLoader());
332             try {
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));
337                 try {
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);
342                 }
343             }
344         }
345     }
346
347     /**
348      * Append class to class pool if doesn't exist.
349      *
350      * @param loader
351      *            - class loader of search class
352      */
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);
358         }
359     }
360
361     /**
362      * Ensure if is class in class loader.
363      *
364      * @param child
365      *            - search class
366      */
367     public void ensureClassLoader(final Class<?> child) {
368         appendClassLoaderIfMissing(child.getClassLoader());
369     }
370 }