Merge "BUG-1276: fixed generated union constructor"
[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 java.util.Collection;
11 import java.util.Map;
12 import java.util.WeakHashMap;
13 import java.util.concurrent.locks.Lock;
14 import java.util.concurrent.locks.ReentrantLock;
15
16 import javassist.CannotCompileException;
17 import javassist.ClassClassPath;
18 import javassist.ClassPath;
19 import javassist.ClassPool;
20 import javassist.CtClass;
21 import javassist.CtField;
22 import javassist.CtMethod;
23 import javassist.LoaderClassPath;
24 import javassist.Modifier;
25 import javassist.NotFoundException;
26 import org.slf4j.Logger;
27 import org.slf4j.LoggerFactory;
28
29 import com.google.common.base.Preconditions;
30
31 public final class JavassistUtils {
32     private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
33
34     private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
35     private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
36     private final Lock lock = new ReentrantLock();
37     private final ClassPool classPool;
38
39     /**
40      * @deprecated Use {@link #forClassPool(ClassPool)} instead.
41      *
42      * This class provides auto-loading into the classpool. Unfortunately reusing
43      * the same class pool with multiple instances can lead the same classpath
44      * being added multiple times, which lowers performance and leaks memory.
45      */
46     @Deprecated
47     public JavassistUtils(final ClassPool pool) {
48         this(pool, null);
49     }
50
51     private JavassistUtils(final ClassPool pool, final Object dummy) {
52         // FIXME: Remove 'dummy' once deprecated constructor is removed
53         classPool = Preconditions.checkNotNull(pool);
54     }
55
56     /**
57      * Get a utility instance for a particular class pool. A new instance is
58      * created if this is a new pool. If an instance already exists, is is
59      * returned.
60      *
61      * @param pool Backing class pool
62      * @return shared utility instance for specified pool
63      */
64     public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
65         JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
66         if (ret == null) {
67             ret = new JavassistUtils(pool, null);
68             INSTANCES.put(pool, ret);
69         }
70         return ret;
71     }
72
73     public Lock getLock() {
74         return lock;
75     }
76
77     public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
78             final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
79         final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
80         final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
81
82         final CtMethod method = _ctMethod;
83         function1.process(method);
84         it.addMethod(method);
85     }
86
87     public void method(final CtClass it, final Class<? extends Object> returnType, final String name,
88             final Collection<? extends Class<?>> parameters, final MethodGenerator function1) throws CannotCompileException {
89         final CtClass[] pa = new CtClass[parameters.size()];
90
91         int i = 0;
92         for (Class<? extends Object> parameter : parameters) {
93             pa[i] = asCtClass(parameter);
94             ++i;
95         }
96
97         final CtMethod method = new CtMethod(asCtClass(returnType), name, pa, it);
98         function1.process(method);
99         it.addMethod(method);
100     }
101
102     public void staticMethod(final CtClass it, final Class<? extends Object> returnType, final String name,
103             final Class<? extends Object> parameter, final MethodGenerator function1) throws CannotCompileException {
104         final CtClass[] pa = new CtClass[] { asCtClass(parameter) };
105         final CtMethod _ctMethod = new CtMethod(asCtClass(returnType), name, pa, it);
106         final CtMethod method = _ctMethod;
107         function1.process(method);
108         it.addMethod(method);
109     }
110
111     public void implementMethodsFrom(final CtClass target, final CtClass source, final MethodGenerator function1) throws CannotCompileException {
112         for (CtMethod method : source.getMethods()) {
113             if (method.getDeclaringClass() == source) {
114                 CtMethod redeclaredMethod = new CtMethod(method, target, null);
115                 function1.process(redeclaredMethod);
116                 target.addMethod(redeclaredMethod);
117             }
118         }
119     }
120
121     public CtClass createClass(final String fqn, final ClassGenerator cls) {
122         CtClass target = classPool.makeClass(fqn);
123         cls.process(target);
124         return target;
125     }
126
127     public CtClass createClass(final String fqn, final CtClass superInterface, final ClassGenerator cls) {
128         CtClass target = classPool.makeClass(fqn);
129         implementsType(target, superInterface);
130         cls.process(target);
131         return target;
132     }
133
134     public void implementsType(final CtClass it, final CtClass supertype) {
135         Preconditions.checkArgument(supertype.isInterface(), "Supertype must be interface");
136         it.addInterface(supertype);
137     }
138
139     public CtClass asCtClass(final Class<? extends Object> class1) {
140         return get(this.classPool, class1);
141     }
142
143     public CtField field(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
144         final CtField field = new CtField(asCtClass(returnValue), name, it);
145         field.setModifiers(Modifier.PUBLIC);
146         it.addField(field);
147         return field;
148     }
149
150     public CtField staticField(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
151         return staticField(it, name, returnValue, null);
152     }
153
154     public CtField staticField(final CtClass it, final String name,
155                                final Class<? extends Object> returnValue,
156                                SourceCodeGenerator sourceGenerator) throws CannotCompileException {
157         final CtField field = new CtField(asCtClass(returnValue), name, it);
158         field.setModifiers(Modifier.PUBLIC + Modifier.STATIC);
159         it.addField(field);
160
161         if (sourceGenerator != null) {
162             sourceGenerator.appendField(field, null);
163         }
164
165         return field;
166     }
167
168     public CtClass get(final ClassPool pool, final Class<? extends Object> cls) {
169         try {
170             return pool.get(cls.getName());
171         } catch (NotFoundException nfe1) {
172             appendClassLoaderIfMissing(cls.getClassLoader());
173             try {
174                 return pool.get(cls.getName());
175             } catch (final NotFoundException nfe2) {
176                 LOG.warn("Appending ClassClassPath for {}", cls, nfe2);
177                 pool.appendClassPath(new ClassClassPath(cls));
178                 try {
179                     return pool.get(cls.getName());
180                 } catch (NotFoundException e) {
181                     LOG.warn("Failed to load class {} from pool {}", cls, pool, e);
182                     throw new IllegalStateException("Failed to load class", e);
183                 }
184             }
185         }
186     }
187
188     public synchronized void appendClassLoaderIfMissing(final ClassLoader loader) {
189         if (!loaderClassPaths.containsKey(loader)) {
190             final ClassPath ctLoader = new LoaderClassPath(loader);
191             classPool.appendClassPath(ctLoader);
192             loaderClassPaths.put(loader, ctLoader);
193         }
194     }
195
196     public void ensureClassLoader(final Class<?> child) {
197         appendClassLoaderIfMissing(child.getClassLoader());
198     }
199 }