2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.sal.binding.generator.util;
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import java.util.Collection;
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;
29 * Users of this utility class are expected to synchronize on this instance
30 * it they need to ensure atomic operations on it.
32 public final class JavassistUtils {
33 private static final Logger LOG = LoggerFactory.getLogger(JavassistUtils.class);
35 private static final Map<ClassPool, JavassistUtils> INSTANCES = new WeakHashMap<>();
36 private final Map<ClassLoader, ClassPath> loaderClassPaths = new WeakHashMap<>();
37 private final ClassPool classPool;
39 private JavassistUtils(final ClassPool pool) {
40 classPool = Preconditions.checkNotNull(pool);
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
48 * @param pool Backing class pool
49 * @return shared utility instance for specified pool
51 public static synchronized JavassistUtils forClassPool(final ClassPool pool) {
52 JavassistUtils ret = INSTANCES.get(Preconditions.checkNotNull(pool));
54 ret = new JavassistUtils(pool);
55 INSTANCES.put(pool, ret);
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);
65 final CtMethod method = _ctMethod;
66 function1.process(method);
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()];
75 for (Class<? extends Object> parameter : parameters) {
76 pa[i] = asCtClass(parameter);
80 final CtMethod method = new CtMethod(asCtClass(returnType), name, pa, it);
81 function1.process(method);
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);
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);
104 public CtClass createClass(final String fqn, final ClassGenerator cls) throws CannotCompileException {
105 CtClass target = classPool.makeClass(fqn);
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);
118 * Instantiate a new class based on a prototype. The class is set to automatically
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
128 public synchronized CtClass instantiatePrototype(final String prototype, final String fqn, final ClassCustomizer customizer) throws NotFoundException {
129 final CtClass result = classPool.getAndRename(prototype, fqn);
131 customizer.customizeClass(result);
132 } catch (Exception e) {
133 LOG.warn("Failed to customize {} from prototype {}", fqn, prototype, e);
135 throw new IllegalStateException(String.format("Failed to instantiate prototype %s as %s", prototype, fqn), e);
138 result.stopPruning(false);
142 public void implementsType(final CtClass it, final CtClass supertype) {
143 Preconditions.checkArgument(supertype.isInterface(), "Supertype must be interface");
144 it.addInterface(supertype);
147 public CtClass asCtClass(final Class<? extends Object> class1) {
148 return get(this.classPool, class1);
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);
158 public CtField staticField(final CtClass it, final String name, final Class<? extends Object> returnValue) throws CannotCompileException {
159 return staticField(it, name, returnValue, null);
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);
169 if (sourceGenerator != null) {
170 sourceGenerator.appendField(field, null);
176 public CtClass get(final ClassPool pool, final Class<? extends Object> cls) {
178 return pool.get(cls.getName());
179 } catch (NotFoundException nfe1) {
180 appendClassLoaderIfMissing(cls.getClassLoader());
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));
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);
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);
204 public void ensureClassLoader(final Class<?> child) {
205 appendClassLoaderIfMissing(child.getClassLoader());