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