KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > cglib > core > AbstractClassGenerator


1 /*
2  * Copyright 2003,2004 The Apache Software Foundation
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */

16 package net.sf.cglib.core;
17
18 import java.io.*;
19 import java.util.*;
20 import java.lang.ref.*;
21 import org.objectweb.asm.ClassReader;
22 import org.objectweb.asm.ClassVisitor;
23 import org.objectweb.asm.ClassWriter;
24 import org.objectweb.asm.Type;
25
26 /**
27  * Abstract class for all code-generating CGLIB utilities.
28  * In addition to caching generated classes for performance, it provides hooks for
29  * customizing the <code>ClassLoader</code>, name of the generated class, and transformations
30  * applied before generation.
31  */

32 abstract public class AbstractClassGenerator
33 implements ClassGenerator
34 {
35     private static final Object JavaDoc NAME_KEY = new Object JavaDoc();
36     private static final ThreadLocal JavaDoc CURRENT = new ThreadLocal JavaDoc();
37
38     private GeneratorStrategy strategy = DefaultGeneratorStrategy.INSTANCE;
39     private NamingPolicy namingPolicy = DefaultNamingPolicy.INSTANCE;
40     private Source source;
41     private ClassLoader JavaDoc classLoader;
42     private String JavaDoc namePrefix;
43     private Object JavaDoc key;
44     private boolean useCache = true;
45     private String JavaDoc className;
46     private boolean attemptLoad;
47
48     protected static class Source {
49         String JavaDoc name;
50         Map cache = new WeakHashMap();
51         public Source(String JavaDoc name) {
52             this.name = name;
53         }
54     }
55
56     protected AbstractClassGenerator(Source source) {
57         this.source = source;
58     }
59
60     protected void setNamePrefix(String JavaDoc namePrefix) {
61         this.namePrefix = namePrefix;
62     }
63
64     final protected String JavaDoc getClassName() {
65         if (className == null)
66             className = getClassName(getClassLoader());
67         return className;
68     }
69
70     private String JavaDoc getClassName(final ClassLoader JavaDoc loader) {
71         final Set nameCache = getClassNameCache(loader);
72         return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {
73             public boolean evaluate(Object JavaDoc arg) {
74                 return nameCache.contains(arg);
75             }
76         });
77     }
78
79     private Set getClassNameCache(ClassLoader JavaDoc loader) {
80         return (Set)((Map)source.cache.get(loader)).get(NAME_KEY);
81     }
82
83     /**
84      * Set the <code>ClassLoader</code> in which the class will be generated.
85      * Concrete subclasses of <code>AbstractClassGenerator</code> (such as <code>Enhancer</code>)
86      * will try to choose an appropriate default if this is unset.
87      * <p>
88      * Classes are cached per-<code>ClassLoader</code> using a <code>WeakHashMap</code>, to allow
89      * the generated classes to be removed when the associated loader is garbage collected.
90      * @param classLoader the loader to generate the new class with, or null to use the default
91      */

92     public void setClassLoader(ClassLoader JavaDoc classLoader) {
93         this.classLoader = classLoader;
94     }
95
96     /**
97      * Override the default naming policy.
98      * @see DefaultNamingPolicy
99      * @param namingPolicy the custom policy, or null to use the default
100      */

101     public void setNamingPolicy(NamingPolicy namingPolicy) {
102         if (namingPolicy == null)
103             namingPolicy = DefaultNamingPolicy.INSTANCE;
104         this.namingPolicy = namingPolicy;
105     }
106
107     /**
108      * @see #setNamingPolicy
109      */

110     public NamingPolicy getNamingPolicy() {
111         return namingPolicy;
112     }
113
114     /**
115      * Whether use and update the static cache of generated classes
116      * for a class with the same properties. Default is <code>true</code>.
117      */

118     public void setUseCache(boolean useCache) {
119         this.useCache = useCache;
120     }
121
122     /**
123      * @see #setUseCache
124      */

125     public boolean getUseCache() {
126         return useCache;
127     }
128
129     /**
130      * If set, CGLIB will attempt to load classes from the specified
131      * <code>ClassLoader</code> before generating them. Because generated
132      * class names are not guaranteed to be unique, the default is <code>false</code>.
133      */

134     public void setAttemptLoad(boolean attemptLoad) {
135         this.attemptLoad = attemptLoad;
136     }
137
138     public boolean getAttemptLoad() {
139         return attemptLoad;
140     }
141     
142     /**
143      * Set the strategy to use to create the bytecode from this generator.
144      * By default an instance of {@see DefaultGeneratorStrategy} is used.
145      */

146     public void setStrategy(GeneratorStrategy strategy) {
147         if (strategy == null)
148             strategy = DefaultGeneratorStrategy.INSTANCE;
149         this.strategy = strategy;
150     }
151
152     /**
153      * @see #setStrategy
154      */

155     public GeneratorStrategy getStrategy() {
156         return strategy;
157     }
158
159     /**
160      * Used internally by CGLIB. Returns the <code>AbstractClassGenerator</code>
161      * that is being used to generate a class in the current thread.
162      */

163     public static AbstractClassGenerator getCurrent() {
164         return (AbstractClassGenerator)CURRENT.get();
165     }
166
167     public ClassLoader JavaDoc getClassLoader() {
168         ClassLoader JavaDoc t = classLoader;
169         if (t == null) {
170             t = getDefaultClassLoader();
171         }
172         if (t == null) {
173             t = getClass().getClassLoader();
174         }
175         if (t == null) {
176             t = Thread.currentThread().getContextClassLoader();
177         }
178         if (t == null) {
179             throw new IllegalStateException JavaDoc("Cannot determine classloader");
180         }
181         return t;
182     }
183
184     abstract protected ClassLoader JavaDoc getDefaultClassLoader();
185
186     protected Object JavaDoc create(Object JavaDoc key) {
187         try {
188             Class JavaDoc gen = null;
189             
190             synchronized (source) {
191                 ClassLoader JavaDoc loader = getClassLoader();
192                 Map cache2 = null;
193                 cache2 = (Map)source.cache.get(loader);
194                 if (cache2 == null) {
195                     cache2 = new HashMap();
196                     cache2.put(NAME_KEY, new HashSet());
197                     source.cache.put(loader, cache2);
198                 } else if (useCache) {
199                     Reference ref = (Reference)cache2.get(key);
200                     gen = (Class JavaDoc) (( ref == null ) ? null : ref.get());
201                 }
202                 if (gen == null) {
203                     Object JavaDoc save = CURRENT.get();
204                     CURRENT.set(this);
205                     try {
206                         this.key = key;
207                         
208                         if (attemptLoad) {
209                             try {
210                                 gen = loader.loadClass(getClassName());
211                             } catch (ClassNotFoundException JavaDoc e) {
212                                 // ignore
213
}
214                         }
215                         if (gen == null) {
216                             byte[] b = strategy.generate(this);
217                             String JavaDoc className = ClassNameReader.getClassName(new ClassReader(b));
218                             getClassNameCache(loader).add(className);
219                             gen = ReflectUtils.defineClass(className, b, loader);
220                         }
221                        
222                         if (useCache) {
223                             cache2.put(key, new WeakReference(gen));
224                         }
225                         return firstInstance(gen);
226                     } finally {
227                         CURRENT.set(save);
228                     }
229                 }
230             }
231             return firstInstance(gen);
232         } catch (RuntimeException JavaDoc e) {
233             throw e;
234         } catch (Error JavaDoc e) {
235             throw e;
236         } catch (Exception JavaDoc e) {
237             throw new CodeGenerationException(e);
238         }
239     }
240
241     abstract protected Object JavaDoc firstInstance(Class JavaDoc type) throws Exception JavaDoc;
242     abstract protected Object JavaDoc nextInstance(Object JavaDoc instance) throws Exception JavaDoc;
243 }
244
Popular Tags