Fixed some major sonar issues in yang-validation-tool
[yangtools.git] / code-generator / binding-generator-impl / src / main / java / org / opendaylight / yangtools / sal / binding / generator / util / JavassistUtils.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. 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.yangtools.sal.binding.generator.util;
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
30  * it they need to ensure atomic operations on it.
31  */
32 public final class JavassistUtils {
33     private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
34
35     private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
36     private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
37     private final ClassPool classPool;
38
39     private JavassistUtils(final ClassPool pool) {
40         classPool = Preconditions.checkNotNull(pool);
41     }
42
43     /**
44      * Get a utility instance for a particular class pool. A new instance is
45      * created if this is a new pool. If an instance already exists, is is
46      * returned.
47      *
48      * @param pool Backing class pool
49      * @return shared utility instance for specified pool
50      */
51     public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
52         JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
53         if (ret == null) {
54             ret = new JavassistUtils(pool);
55             INSTANCES.put(pool, ret);
56         }
57         return ret;
58     }
59
60     public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
61             final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
62         final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
63         final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
64
65         final CtMethod method = _ctMethod;
66         function1.process(method);
67         it.addMethod(method);
68     }
69
70     public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
71             final Collection<? extends Class<?>> parameters, final MethodGenerator function1) throws CannotCompileException {
72         final CtClass[] pa = new CtClass[parameters.size()];
73
74         int i = 0;
75         for (Class<? extends Object> parameter : parameters) {
76             pa[i] = asCtClass(parameter);
77             ++i;
78         }
79
80         final CtMethod method = new CtMethod(asCtClass(returnType), name, pa, it);
81         function1.process(method);
82         it.addMethod(method);
83     }
84
85     public void staticMethod(final CtClass it, final Class<? extends Object> returnType, final String name,
86             final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
87         final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
88         final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
89         final CtMethod method = _ctMethod;
90         function1.process(method);
91         it.addMethod(method);
92     }
93
94     public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator function1) throws CannotCompileException {
95         for (CtMethod method : source.getMethods()) {
96             if (method.getDeclaringClass() == source) {
97                 CtMethod redeclaredMethod = new CtMethod(method, target, null);
98                 function1.process(redeclaredMethod);
99                 target.addMethod(redeclaredMethod);
100             }
101         }
102     }
103
104     public CtClass createClass(final String fqn, final ClassGenerator cls) throws CannotCompileException {
105         CtClass target = classPool.makeClass(fqn);
106         cls.process(target);
107         return target;
108     }
109
110     public CtClass createClass(final String fqn, final CtClass superInterface, final ClassGenerator cls) throws CannotCompileException {
111         CtClass target = classPool.makeClass(fqn);
112         implementsType(target, superInterface);
113         cls.process(target);
114         return target;
115     }
116
117     /**
118      * Instantiate a new class based on a prototype. The class is set to automatically
119      * prune.
120      *
121      * @param prototype Prototype class fully qualified name
122      * @param fqn Target class fully qualified name
123      * @param customizer Customization callback to be invoked on the new class
124      * @return An instance of the new class
125      * @throws NotFoundException when the prototype class is not found
126      */
127     @Beta
128     public synchronized CtClass instantiatePrototype(final String prototype, final String fqn, final ClassCustomizer customizer) throws NotFoundException {
129         final CtClass result = classPool.getAndRename(prototype, fqn);
130         try {
131             customizer.customizeClass(result);
132         } catch (Exception e) {
133             LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
134             result.detach();
135             throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn), e);
136         }
137
138         result.stopPruning(false);
139         return result;
140     }
141
142     public void implementsType(final CtClass it, final CtClass supertype) {
143         Preconditions.checkArgument(supertype.isInterface(), "Supertype must be interface");
144         it.addInterface(supertype);
145     }
146
147     public CtClass asCtClass(final Class<? extends Object> class1) {
148         return get(this.classPool, class1);
149     }
150
151     public CtField field(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
152         final CtField field = new CtField(asCtClass(returnValue), name, it);
153         field.setModifiers(Modifier.PUBLIC);
154         it.addField(field);
155         return field;
156     }
157
158     public CtField staticField(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
159         return staticField(it, name, returnValue, null);
160     }
161
162     public CtField staticField(final CtClass it, final String name,
163             final Class<? extends Object> returnValue,
164             final SourceCodeGenerator sourceGenerator) throws CannotCompileException {
165         final CtField field = new CtField(asCtClass(returnValue), name, it);
166         field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
167         it.addField(field);
168
169         if (sourceGenerator != null) {
170             sourceGenerator.appendField(field, null);
171         }
172
173         return field;
174     }
175
176     public CtClass get(final ClassPool pool, final Class<? extends Object> cls) {
177         try {
178             return pool.get(cls.getName());
179         } catch (NotFoundException nfe1) {
180             appendClassLoaderIfMissing(cls.getClassLoader());
181             try {
182                 return pool.get(cls.getName());
183             } catch (final NotFoundException nfe2) {
184                 LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
185                 pool.appendClassPath(new ClassClassPath(cls));
186                 try {
187                     return pool.get(cls.getName());
188                 } catch (NotFoundException e) {
189                     LOG.warn("Failed to load class {} from pool {}", cls, pool, e);
190                     throw new IllegalStateException("Failed to load class", e);
191                 }
192             }
193         }
194     }
195
196     public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
197         if (!loaderClassPaths.containsKey(loader)) {
198             final ClassPath ctLoader = new LoaderClassPath(loader);
199             classPool.appendClassPath(ctLoader);
200             loaderClassPaths.put(loader, ctLoader);
201         }
202     }
203
204     public void ensureClassLoader(final Class<?> child) {
205         appendClassLoaderIfMissing(child.getClassLoader());
206     }
207 }