All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.groovy.runtime.metaclass.MetaClassRegistryImpl Maven / Gradle / Ivy

There is a newer version: 3.0.22
Show newest version
/*
 * Copyright 2003-2007 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.codehaus.groovy.runtime.metaclass;

import groovy.lang.*;

import org.codehaus.groovy.reflection.*;
import org.codehaus.groovy.runtime.*;
import org.codehaus.groovy.vmplugin.VMPluginFactory;
import org.codehaus.groovy.util.FastArray;
import org.codehaus.groovy.util.ManagedLinkedList;
import org.codehaus.groovy.util.ReferenceBundle;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;

/**
 * A registry of MetaClass instances which caches introspection &
 * reflection information and allows methods to be dynamically added to
 * existing classes at runtime
 *
 * @author James Strachan
 * @author John Wilson
 * @author Jochen Theodorou
 * @author Graeme Rocher
 * @author Alex Tkachman
 *
 * @version $Revision: 18003 $
 */
public class MetaClassRegistryImpl implements MetaClassRegistry{
    private boolean useAccessible;

    private FastArray instanceMethods = new FastArray();
    private FastArray staticMethods = new FastArray();

    private LinkedList changeListenerList = new LinkedList();
    private ManagedLinkedList metaClassInfo = new ManagedLinkedList(ReferenceBundle.getWeakBundle());

    public static final int LOAD_DEFAULT = 0;
    public static final int DONT_LOAD_DEFAULT = 1;
    private static MetaClassRegistry instanceInclude;
    private static MetaClassRegistry instanceExclude;

    public MetaClassRegistryImpl() {
        this(LOAD_DEFAULT, true);
    }

    public MetaClassRegistryImpl(int loadDefault) {
        this(loadDefault, true);
    }

    /**
     * @param useAccessible defines whether or not the {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)}
     *                      method will be called to enable access to all methods when using reflection
     */
    public MetaClassRegistryImpl(boolean useAccessible) {
        this(LOAD_DEFAULT, useAccessible);
    }
    
    public MetaClassRegistryImpl(final int loadDefault, final boolean useAccessible) {
        this.useAccessible = useAccessible;

        if (loadDefault == LOAD_DEFAULT) {
            Map> map = new HashMap>();

            // let's register the default methods
            registerMethods(DefaultGroovyMethods.class, true, true, map);
            registerMethods(SwingGroovyMethods.class, false, true, map);
            registerMethods(SqlGroovyMethods.class, false, true, map);
            registerMethods(XmlGroovyMethods.class, false, true, map);
            Class[] pluginDGMs = VMPluginFactory.getPlugin().getPluginDefaultGroovyMethods();
            for (Class plugin : pluginDGMs) {
                registerMethods(plugin, false, true, map);
            }
            registerMethods(DefaultGroovyStaticMethods.class, false, false, map);

            for (Map.Entry> e : map.entrySet()) {
                CachedClass cls = e.getKey();
                cls.setNewMopMethods(e.getValue());
            }
        }

        installMetaClassCreationHandle();

        final MetaClass emcMetaClass = metaClassCreationHandle.create(ExpandoMetaClass.class, this);
        emcMetaClass.initialize();
        ClassInfo.getClassInfo(ExpandoMetaClass.class).setStrongMetaClass(emcMetaClass);
        

        addMetaClassRegistryChangeEventListener(new MetaClassRegistryChangeEventListener(){
            public void updateConstantMetaClass(MetaClassRegistryChangeEvent cmcu) {
                synchronized (metaClassInfo) {
                   metaClassInfo.add(cmcu.getNewMetaClass());
                }
            }
        });
   }

    /**
     * Looks for a class called 'groovy.runtime.metaclass.CustomMetaClassCreationHandle' and if it exists uses it as the MetaClassCreationHandle
     * otherwise uses the default
     *
     * @see groovy.lang.MetaClassRegistry.MetaClassCreationHandle
     */
    private void installMetaClassCreationHandle() {
	       try {
	           final Class customMetaClassHandle = Class.forName("groovy.runtime.metaclass.CustomMetaClassCreationHandle");
	           final Constructor customMetaClassHandleConstructor = customMetaClassHandle.getConstructor(new Class[]{});
				 this.metaClassCreationHandle = (MetaClassCreationHandle)customMetaClassHandleConstructor.newInstance();
	       } catch (final ClassNotFoundException e) {
	           this.metaClassCreationHandle = new MetaClassCreationHandle();
	       } catch (final Exception e) {
	           throw new GroovyRuntimeException("Could not instantiate custom Metaclass creation handle: "+ e, e);
	       }
    }
    
    private void registerMethods(final Class theClass, final boolean useMethodWrapper, final boolean useInstanceMethods, Map> map) {
        CachedMethod[] methods = ReflectionCache.getCachedClass(theClass).getMethods();

        if (useMethodWrapper) {
            // Here we instantiate objects representing MetaMethods for DGM methods.
            // Calls for such meta methods done without reflection, so more effectively.
            // It gives 7-8% improvement for benchmarks involving just several arithmetic operations
            for (int i = 0; ; ++i) {
                try {
                    final String className = "org.codehaus.groovy.runtime.dgm$" + i;
                    final Class aClass = Class.forName(className);
                    createMetaMethodFromClass(map, aClass);
                } catch (ClassNotFoundException e){
                    break;
                }
            }

            final Class[] additionals = DefaultGroovyMethods.additionals;
            for (int i = 0; i != additionals.length; ++i ) {
                createMetaMethodFromClass(map, additionals[i]);
            }
        } else {
            for (CachedMethod method : methods) {
                final int mod = method.getModifiers();
                if (Modifier.isStatic(mod) && Modifier.isPublic(mod) && method.getCachedMethod().getAnnotation(Deprecated.class) == null) {
                    CachedClass[] paramTypes = method.getParameterTypes();
                    if (paramTypes.length > 0) {
                        List arr = map.get(paramTypes[0]);
                        if (arr == null) {
                            arr = new ArrayList(4);
                            map.put(paramTypes[0], arr);
                        }
                        if (useInstanceMethods) {
                            final NewInstanceMetaMethod metaMethod = new NewInstanceMetaMethod(method);
                            arr.add(metaMethod);
                            instanceMethods.add(metaMethod);
                        } else {
                            final NewStaticMetaMethod metaMethod = new NewStaticMetaMethod(method);
                            arr.add(metaMethod);
                            staticMethods.add(metaMethod);
                        }
                    }
                }
            }
        }
    }

    private void createMetaMethodFromClass(Map> map, Class aClass) {
        try {
            MetaMethod method = (MetaMethod) aClass.newInstance();
            final CachedClass declClass = method.getDeclaringClass();
            List arr = map.get(declClass);
            if (arr == null) {
                arr = new ArrayList(4);
                map.put(declClass, arr);
            }
            arr.add(method);
            instanceMethods.add(method);
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        }
    }

    public final MetaClass getMetaClass(Class theClass) {
        return ClassInfo.getClassInfo(theClass).getMetaClass();
    }

    public MetaClass getMetaClass(Object obj) {
        return ClassInfo.getClassInfo(obj.getClass()).getMetaClass(obj);
    }

    /**
     * if oldMc is null, newMc will replace whatever meta class was used before.
     * if oldMc is not null, then newMc will be used only if he stored mc is
     * the same as oldMc
     */
    private void setMetaClass(Class theClass, MetaClass oldMc, MetaClass newMc) {
        final ClassInfo info = ClassInfo.getClassInfo(theClass);
        
        MetaClass mc = null;
        info.lock();
        try {        	
        	if (oldMc!=null) mc=info.getStrongMetaClass();
        	// mc==null means that mc will be null too, so the 
        	// condition is always fulfilled. 
        	if (mc==oldMc) info.setStrongMetaClass(newMc);
        } finally {
            info.unlock();
        }

        if (oldMc!=mc) fireConstantMetaClassUpdate(theClass,newMc);
    }
    
    public void removeMetaClass(Class theClass) {
    	setMetaClass(theClass, null, null);
    }
    
    /**
     * Registers a new MetaClass in the registry to customize the type
     *
     * @param theClass
     * @param theMetaClass
     */
    public void setMetaClass(Class theClass, MetaClass theMetaClass) {
        setMetaClass(theClass,null,theMetaClass);
    }


    public void setMetaClass(Object obj, MetaClass theMetaClass) {
        Class theClass = obj.getClass ();
        final ClassInfo info = ClassInfo.getClassInfo(theClass);

        info.lock();
        try {
            info.setPerInstanceMetaClass(obj, theMetaClass);
        }
        finally {
            info.unlock();
        }
        
        fireConstantMetaClassUpdate(theClass, theMetaClass);
    }


    public boolean useAccessible() {
        return useAccessible;
    }

    // the following is experimental code, not intended for stable use yet
    private volatile MetaClassCreationHandle metaClassCreationHandle = new MetaClassCreationHandle();
    
    /**
     * Gets a handle internally used to create MetaClass implementations
     * WARNING: experimental code, likely to change soon
     * @return the handle
     */    
    public MetaClassCreationHandle getMetaClassCreationHandler() {
        return metaClassCreationHandle;
    }
    
    /**
     * Sets a handle internally used to create MetaClass implementations.
     * When replacing the handle with a custom version, you should
     * reuse the old handle to keep custom logic and to use the
     * default logic as fall back.
     * WARNING: experimental code, likely to change soon
     * @param handle the handle
     */
    public void setMetaClassCreationHandle(MetaClassCreationHandle handle) {
		if(handle == null) throw new IllegalArgumentException("Cannot set MetaClassCreationHandle to null value!");
        ClassInfo.clearModifiedExpandos();
        metaClassCreationHandle = handle;
    }    

    /**
     * Adds a listener for constant meta classes.
     * @param listener the listener
     */
    public void addMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener) {
        synchronized (changeListenerList) {
            changeListenerList.add(listener);
        }
    }

    /**
     * Removes a constant meta class listener.
     * @param listener the listener
     */
    public void removeMetaClassRegistryChangeEventListener(MetaClassRegistryChangeEventListener listener) {
    	synchronized (changeListenerList) {
        	Object first = changeListenerList.getFirst();
            changeListenerList.remove(listener);
            // we want to keep the first entry!
            if (changeListenerList.size()==0) changeListenerList.addFirst(first); 
        }
    }

    /**
     * Causes the execution of all registered listeners. This method is used mostly
     * internal to kick of the listener notification. It can also be used by subclasses
     * to achieve the same.
     * 
     * @param c the class
     * @param newMc the new MetaClass
     */
    protected void fireConstantMetaClassUpdate(Class c, MetaClass newMc) {
        MetaClassRegistryChangeEventListener[]  listener = getMetaClassRegistryChangeEventListeners();
        MetaClassRegistryChangeEvent cmcu = new MetaClassRegistryChangeEvent(this,c,newMc);
        for (int i=0; i




© 2015 - 2024 Weber Informatics LLC | Privacy Policy