edee29cf6b374df9d3b2f127f8d030e9f3acf56d
[fedora-idea.git] / platform / platform-impl / src / net / sf / cglib / core / AbstractClassGenerator.java
blobedee29cf6b374df9d3b2f127f8d030e9f3acf56d
1 /*
2 * Copyright 2003,2004 The Apache Software Foundation
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package net.sf.cglib.core;
18 import net.sf.cglib.asm.ClassReader;
20 import java.lang.ref.Reference;
21 import java.lang.ref.WeakReference;
22 import java.util.*;
24 /**
25 * Abstract class for all code-generating CGLIB utilities.
26 * In addition to caching generated classes for performance, it provides hooks for
27 * customizing the <code>ClassLoader</code>, name of the generated class, and transformations
28 * applied before generation.
30 * intellij changes: made some fields final
32 abstract public class AbstractClassGenerator
33 implements ClassGenerator
35 private static final Object NAME_KEY = new Object();
36 private static final ThreadLocal CURRENT = new ThreadLocal();
38 private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
39 private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
40 //change by Peter: make fields final
41 private final Source source;
42 private ClassLoader classLoader;
43 private String namePrefix;
44 private Object key;
45 private boolean useCache = true;
46 private String className;
47 private boolean attemptLoad;
49 protected static class Source {
50 //change by Peter: made fields final
51 final String name;
52 final Map cache = new WeakHashMap();
53 public Source(String name) {
54 this.name = name;
58 protected AbstractClassGenerator(Source source) {
59 this.source = source;
62 protected void setNamePrefix(String namePrefix) {
63 this.namePrefix = namePrefix;
66 final protected String getClassName() {
67 if (className == null)
68 className = getClassName(getClassLoader());
69 return className;
72 private String getClassName(final ClassLoader loader) {
73 final Set nameCache = getClassNameCache(loader);
74 return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {
75 public boolean evaluate(Object arg) {
76 return nameCache.contains(arg);
78 });
81 private Set getClassNameCache(ClassLoader loader) {
82 return (Set)((Map)source.cache.get(loader)).get(NAME_KEY);
85 /**
86 * Set the <code>ClassLoader</code> in which the class will be generated.
87 * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>)
88 * will try to choose an appropriate default if this is unset.
89 * <p>
90 * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow
91 * the generated classes to be removed when the associated loader is garbage collected.
92 * @param classLoader the loader to generate the new class with, or null to use the default
94 public void setClassLoader(ClassLoader classLoader) {
95 this.classLoader = classLoader;
98 /**
99 * Override the default naming policy.
100 * @see DefaultNamingPolicy
101 * @param namingPolicy the custom policy, or null to use the default
103 public void setNamingPolicy(NamingPolicy namingPolicy) {
104 if (namingPolicy == null)
105 namingPolicy = DefaultNamingPolicy.INSTANCE;
106 this.namingPolicy = namingPolicy;
110 * @see #setNamingPolicy
112 public NamingPolicy getNamingPolicy() {
113 return namingPolicy;
117 * Whether use and update the static cache of generated classes
118 * for a class with the same properties. Default is <code>true</code>.
120 public void setUseCache(boolean useCache) {
121 this.useCache = useCache;
125 * @see #setUseCache
127 public boolean getUseCache() {
128 return useCache;
132 * If set, CGLIB will attempt to load classes from the specified
133 * <code>ClassLoader</code> before generating them. Because generated
134 * class names are not guaranteed to be unique, the default is <code>false</code>.
136 public void setAttemptLoad(boolean attemptLoad) {
137 this.attemptLoad = attemptLoad;
140 public boolean getAttemptLoad() {
141 return attemptLoad;
145 * Set the strategy to use to create the bytecode from this generator.
146 * By default an instance of {@see DefaultGeneratorStrategy} is used.
148 public void setStrategy(GeneratorStrategy strategy) {
149 if (strategy == null)
150 strategy = DefaultGeneratorStrategy.INSTANCE;
151 this.strategy = strategy;
155 * @see #setStrategy
157 public GeneratorStrategy getStrategy() {
158 return strategy;
162 * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code>
163 * that is being used to generate a class in the current thread.
165 public static AbstractClassGenerator getCurrent() {
166 return (AbstractClassGenerator)CURRENT.get();
169 public ClassLoader getClassLoader() {
170 ClassLoader t = classLoader;
171 if (t == null) {
172 t = getDefaultClassLoader();
174 if (t == null) {
175 t = getClass().getClassLoader();
177 if (t == null) {
178 t = Thread.currentThread().getContextClassLoader();
180 if (t == null) {
181 throw new IllegalStateException("Cannot determine classloader");
183 return t;
186 abstract protected ClassLoader getDefaultClassLoader();
188 protected Object create(Object key) {
189 try {
190 Class gen = null;
192 synchronized (source) {
193 ClassLoader loader = getClassLoader();
194 Map cache2 = null;
195 cache2 = (Map)source.cache.get(loader);
196 if (cache2 == null) {
197 cache2 = new HashMap();
198 cache2.put(NAME_KEY, new HashSet());
199 source.cache.put(loader, cache2);
200 } else if (useCache) {
201 Reference ref = (Reference)cache2.get(key);
202 gen = (Class) (( ref == null ) ? null : ref.get());
204 if (gen == null) {
205 Object save = CURRENT.get();
206 CURRENT.set(this);
207 try {
208 this.key = key;
210 if (attemptLoad) {
211 try {
212 gen = loader.loadClass(getClassName());
213 } catch (ClassNotFoundException e) {
214 // ignore
217 if (gen == null) {
218 byte[] b = strategy.generate(this);
219 String className = ClassNameReader.getClassName(new ClassReader(b));
220 getClassNameCache(loader).add(className);
221 gen = ReflectUtils.defineClass(className, b, loader);
224 if (useCache) {
225 cache2.put(key, new WeakReference(gen));
227 return firstInstance(gen);
228 } finally {
229 CURRENT.set(save);
233 return firstInstance(gen);
234 } catch (RuntimeException e) {
235 throw e;
236 } catch (Error e) {
237 throw e;
238 } catch (Exception e) {
239 throw new CodeGenerationException(e);
243 abstract protected Object firstInstance(Class type) throws Exception;
244 abstract protected Object nextInstance(Object instance) throws Exception;