001: /*
002: $Id: MetaClassRegistry.java 4554 2006-12-21 23:54:28Z blackdrag $
003:
004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
005:
006: Redistribution and use of this software and associated documentation
007: ("Software"), with or without modification, are permitted provided
008: that the following conditions are met:
009:
010: 1. Redistributions of source code must retain copyright
011: statements and notices. Redistributions must also contain a
012: copy of this document.
013:
014: 2. Redistributions in binary form must reproduce the
015: above copyright notice, this list of conditions and the
016: following disclaimer in the documentation and/or other
017: materials provided with the distribution.
018:
019: 3. The name "groovy" must not be used to endorse or promote
020: products derived from this Software without prior written
021: permission of The Codehaus. For written permission,
022: please contact info@codehaus.org.
023:
024: 4. Products derived from this Software may not be called "groovy"
025: nor may "groovy" appear in their names without prior written
026: permission of The Codehaus. "groovy" is a registered
027: trademark of The Codehaus.
028:
029: 5. Due credit should be given to The Codehaus -
030: http://groovy.codehaus.org/
031:
032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
043: OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: */
046: package groovy.lang;
047:
048: import java.lang.reflect.Constructor;
049: import java.lang.reflect.Method;
050: import java.security.AccessController;
051: import java.security.PrivilegedAction;
052: import java.util.LinkedList;
053: import java.util.List;
054:
055: import org.codehaus.groovy.classgen.ReflectorGenerator;
056: import org.codehaus.groovy.runtime.DefaultGroovyMethods;
057: import org.codehaus.groovy.runtime.DefaultGroovyStaticMethods;
058: import org.codehaus.groovy.runtime.MethodHelper;
059: import org.codehaus.groovy.runtime.ReferenceMap;
060: import org.codehaus.groovy.runtime.Reflector;
061: import org.codehaus.groovy.runtime.ReflectorLoader;
062: import org.objectweb.asm.ClassWriter;
063:
064: /**
065: * A registery of MetaClass instances which caches introspection &
066: * reflection information and allows methods to be dynamically added to
067: * existing classes at runtime
068: *
069: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
070: * @author John Wilson
071: * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
072: * @version $Revision: 4554 $
073: */
074: public class MetaClassRegistry {
075: private ReferenceMap metaClasses = new ReferenceMap();
076: private ReferenceMap loaderMap = new ReferenceMap();
077: private boolean useAccessible;
078:
079: private LinkedList instanceMethods = new LinkedList();
080: private LinkedList staticMethods = new LinkedList();
081:
082: public static final int LOAD_DEFAULT = 0;
083: public static final int DONT_LOAD_DEFAULT = 1;
084: private static MetaClassRegistry instanceInclude;
085: private static MetaClassRegistry instanceExclude;
086:
087: public MetaClassRegistry() {
088: this (LOAD_DEFAULT, true);
089: }
090:
091: public MetaClassRegistry(int loadDefault) {
092: this (loadDefault, true);
093: }
094:
095: /**
096: * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
097: * method will be called to enable access to all methods when using reflection
098: */
099: public MetaClassRegistry(boolean useAccessible) {
100: this (LOAD_DEFAULT, useAccessible);
101: }
102:
103: public MetaClassRegistry(final int loadDefault,
104: final boolean useAccessible) {
105: this .useAccessible = useAccessible;
106:
107: if (loadDefault == LOAD_DEFAULT) {
108: // lets register the default methods
109: registerMethods(DefaultGroovyMethods.class, true);
110: registerMethods(DefaultGroovyStaticMethods.class, false);
111: }
112: }
113:
114: private void registerMethods(final Class theClass,
115: final boolean useInstanceMethods) {
116: Method[] methods = theClass.getMethods();
117: for (int i = 0; i < methods.length; i++) {
118: Method method = methods[i];
119: if (MethodHelper.isStatic(method)) {
120: Class[] paramTypes = method.getParameterTypes();
121: if (paramTypes.length > 0) {
122: if (useInstanceMethods) {
123: instanceMethods.add(method);
124: } else {
125: staticMethods.add(method);
126: }
127: }
128: }
129: }
130: }
131:
132: public MetaClass getMetaClass(Class theClass) {
133: synchronized (theClass) {
134: MetaClass answer = (MetaClass) metaClasses.get(theClass);
135: if (answer == null) {
136: answer = getMetaClassFor(theClass);
137: answer.initialize();
138: metaClasses.put(theClass, answer);
139: }
140: return answer;
141: }
142: }
143:
144: public void removeMetaClass(Class theClass) {
145: synchronized (theClass) {
146: metaClasses.remove(theClass);
147: }
148: }
149:
150: /**
151: * Registers a new MetaClass in the registry to customize the type
152: *
153: * @param theClass
154: * @param theMetaClass
155: */
156: public void setMetaClass(Class theClass, MetaClass theMetaClass) {
157: synchronized (theClass) {
158: metaClasses.putStrong(theClass, theMetaClass);
159: }
160: }
161:
162: public boolean useAccessible() {
163: return useAccessible;
164: }
165:
166: private ReflectorLoader getReflectorLoader(final ClassLoader loader) {
167: synchronized (loaderMap) {
168: ReflectorLoader reflectorLoader = (ReflectorLoader) loaderMap
169: .get(loader);
170: if (reflectorLoader == null) {
171: reflectorLoader = (ReflectorLoader) AccessController
172: .doPrivileged(new PrivilegedAction() {
173: public Object run() {
174: return new ReflectorLoader(loader);
175: }
176: });
177: loaderMap.put(loader, reflectorLoader);
178: }
179: return reflectorLoader;
180: }
181: }
182:
183: /**
184: * Used by MetaClass when registering new methods which avoids initializing the MetaClass instances on lookup
185: */
186: MetaClass lookup(Class theClass) {
187: synchronized (theClass) {
188: MetaClass answer = (MetaClass) metaClasses.get(theClass);
189: if (answer == null) {
190: answer = getMetaClassFor(theClass);
191: metaClasses.put(theClass, answer);
192: }
193: return answer;
194: }
195: }
196:
197: /**
198: * Find a MetaClass for the class
199: * If there is a custom MetaClass then return an instance of that. Otherwise return an instance of the standard MetaClass
200: *
201: * @param theClass
202: * @return An instace of the MetaClass which will handle this class
203: */
204: private MetaClass getMetaClassFor(final Class theClass) {
205: try {
206: final Class customMetaClass = Class
207: .forName("groovy.runtime.metaclass."
208: + theClass.getName() + "MetaClass");
209: final Constructor customMetaClassConstructor = customMetaClass
210: .getConstructor(new Class[] {
211: MetaClassRegistry.class, Class.class });
212:
213: return (MetaClass) customMetaClassConstructor
214: .newInstance(new Object[] { this , theClass });
215: } catch (final ClassNotFoundException e) {
216: return new MetaClassImpl(this , theClass);
217: } catch (final Exception e) {
218: throw new GroovyRuntimeException(
219: "Could not instantiate custom Metaclass for class: "
220: + theClass.getName() + ". Reason: " + e, e);
221: }
222: }
223:
224: /**
225: * Singleton of MetaClassRegistry. Shall we use threadlocal to store the instance?
226: *
227: * @param includeExtension
228: */
229: public static MetaClassRegistry getInstance(int includeExtension) {
230: if (includeExtension != DONT_LOAD_DEFAULT) {
231: if (instanceInclude == null) {
232: instanceInclude = new MetaClassRegistry();
233: }
234: return instanceInclude;
235: } else {
236: if (instanceExclude == null) {
237: instanceExclude = new MetaClassRegistry(
238: DONT_LOAD_DEFAULT);
239: }
240: return instanceExclude;
241: }
242: }
243:
244: public synchronized Reflector loadReflector(final Class theClass,
245: List methods) {
246: final String name = getReflectorName(theClass);
247: ClassLoader loader = (ClassLoader) AccessController
248: .doPrivileged(new PrivilegedAction() {
249: public Object run() {
250: ClassLoader loader = theClass.getClassLoader();
251: if (loader == null)
252: loader = this .getClass().getClassLoader();
253: return loader;
254: }
255: });
256: final ReflectorLoader rloader = getReflectorLoader(loader);
257: Class ref = rloader.getLoadedClass(name);
258: if (ref == null) {
259: /*
260: * Lets generate it && load it.
261: */
262: ReflectorGenerator generator = new ReflectorGenerator(
263: methods);
264: ClassWriter cw = new ClassWriter(true);
265: generator.generate(cw, name);
266: final byte[] bytecode = cw.toByteArray();
267: ref = (Class) AccessController
268: .doPrivileged(new PrivilegedAction() {
269: public Object run() {
270: return rloader.defineClass(name, bytecode,
271: getClass().getProtectionDomain());
272: }
273: });
274: }
275: try {
276: return (Reflector) ref.newInstance();
277: } catch (Exception e) {
278: throw new GroovyRuntimeException(
279: "Could not generate and load the reflector for class: "
280: + name + ". Reason: " + e, e);
281: }
282: }
283:
284: private String getReflectorName(Class theClass) {
285: String className = theClass.getName();
286: String packagePrefix = "gjdk.";
287: String name = packagePrefix + className + "_GroovyReflector";
288: if (theClass.isArray()) {
289: Class clazz = theClass;
290: name = packagePrefix;
291: int level = 0;
292: while (clazz.isArray()) {
293: clazz = clazz.getComponentType();
294: level++;
295: }
296: String componentName = clazz.getName();
297: name = packagePrefix + componentName
298: + "_GroovyReflectorArray";
299: if (level > 1)
300: name += level;
301: }
302: return name;
303: }
304:
305: List getInstanceMethods() {
306: return instanceMethods;
307: }
308:
309: List getStaticMethods() {
310: return staticMethods;
311: }
312: }
|