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

grails.util.GrailsMetaClassUtils Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2004-2022 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
 *
 *      https://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 grails.util;

import java.lang.reflect.Constructor;
import java.util.List;

import groovy.lang.AdaptingMetaClass;
import groovy.lang.Closure;
import groovy.lang.ClosureInvokingMethod;
import groovy.lang.DelegatingMetaClass;
import groovy.lang.ExpandoMetaClass;
import groovy.lang.GroovyObject;
import groovy.lang.GroovySystem;
import groovy.lang.MetaClass;
import groovy.lang.MetaClassRegistry;
import groovy.lang.MetaMethod;
import groovy.lang.MetaProperty;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.groovy.runtime.metaclass.ThreadManagedMetaBeanProperty;
import org.springframework.beans.BeanUtils;

/**
 * Provides utility methods for working with the Groovy MetaClass API.
 *
 * @author Graeme Rocher
 * @since 0.5
 */
public final class GrailsMetaClassUtils {

    private static final int MAX_DELEGATE_LEVELS = 10;

    private static final Log logger = LogFactory.getLog(GrailsMetaClassUtils.class);

    private static final Object[] NO_ARGS = new Object[0];

    private GrailsMetaClassUtils() {
    }

    /**
     * Retrieves the MetaClassRegistry instance.
     *
     * @return The registry
     */
    public static MetaClassRegistry getRegistry() {
        return GroovySystem.getMetaClassRegistry();
    }

    /**
     * Copies the ExpandoMetaClass dynamic methods and properties from one Class to another.
     *
     * @param fromClass The source class
     * @param toClass  The destination class
     * @param removeSource Whether to remove the source class after completion. True if yes
     */
    @SuppressWarnings({"unchecked", "rawtypes"})
    public static void copyExpandoMetaClass(Class fromClass, Class toClass, boolean removeSource) {
        MetaClassRegistry registry = getRegistry();
        MetaClass oldMetaClass = registry.getMetaClass(fromClass);

        AdaptingMetaClass adapter = null;
        ExpandoMetaClass emc;

        if (oldMetaClass instanceof AdaptingMetaClass) {
            adapter = ((AdaptingMetaClass) oldMetaClass);
            emc = (ExpandoMetaClass) adapter.getAdaptee();
            if (logger.isDebugEnabled()) {
                logger.debug("Obtained adapted MetaClass [" + emc + "] from AdapterMetaClass instance [" + adapter + "]");
            }

            if (removeSource) {
                registry.removeMetaClass(fromClass);
            }
        }
        else {
            emc = (ExpandoMetaClass) oldMetaClass;
            if (logger.isDebugEnabled()) {
                logger.debug("No adapter MetaClass found, using original [" + emc + "]");
            }
        }

        ExpandoMetaClass replacement = new ExpandoMetaClass(toClass, true, true);

        for (Object obj : emc.getExpandoMethods()) {
            if (obj instanceof ClosureInvokingMethod) {
                ClosureInvokingMethod cim = (ClosureInvokingMethod) obj;
                Closure callable = cim.getClosure();
                if (!cim.isStatic()) {
                    replacement.setProperty(cim.getName(), callable);
                }
                else {
                    ((GroovyObject) replacement.getProperty(ExpandoMetaClass.STATIC_QUALIFIER)).setProperty(cim.getName(), callable);
                }
            }
        }

        for (Object o : emc.getExpandoProperties()) {
            if (o instanceof ThreadManagedMetaBeanProperty) {
                ThreadManagedMetaBeanProperty mbp = (ThreadManagedMetaBeanProperty) o;
                replacement.setProperty(mbp.getName(), mbp.getInitialValue());
            }
        }
        replacement.initialize();

        if (adapter == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("Adding MetaClass for class [" + toClass + "] MetaClass [" + replacement + "]");
            }
            registry.setMetaClass(toClass, replacement);
        }
        else {
            if (logger.isDebugEnabled()) {
                logger.debug("Adding MetaClass for class [" + toClass + "] MetaClass [" + replacement +
                        "] with adapter [" + adapter + "]");
            }
            try {
                Constructor c = adapter.getClass().getConstructor(new Class[] { MetaClass.class });
                MetaClass newAdapter = (MetaClass) BeanUtils.instantiateClass(c, new Object[] {replacement});
                registry.setMetaClass(toClass, newAdapter);
            }
            catch (NoSuchMethodException e) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Exception thrown constructing new MetaClass adapter when reloading: " + e.getMessage(), e);
                }
            }
        }
    }

    public static ExpandoMetaClass getExpandoMetaClass(Class aClass) {
        MetaClassRegistry registry = getRegistry();
        MetaClass mc = registry.getMetaClass(aClass);

        if (mc instanceof ExpandoMetaClass) {
            ExpandoMetaClass emc = (ExpandoMetaClass) mc;
            registry.setMetaClass(aClass, emc); // make permanent
            return emc;
        }

        registry.removeMetaClass(aClass);

        mc = registry.getMetaClass(aClass);
        if (mc instanceof ExpandoMetaClass) {
            return (ExpandoMetaClass) mc;
        }

        ExpandoMetaClass emc = new ExpandoMetaClass(aClass, true, true);
        emc.initialize();
        registry.setMetaClass(aClass, emc);
        return emc;
    }

    public static MetaClass getMetaClass(Object instance) {
        if (instance instanceof GroovyObject) {
            GroovyObject groovyObject = (GroovyObject) instance;
            MetaClass metaClass = groovyObject.getMetaClass();
            metaClass = unwrapDelegatingMetaClass(metaClass);

            if (!(metaClass instanceof ExpandoMetaClass)) {
                metaClass = getExpandoMetaClass(instance.getClass());
                groovyObject.setMetaClass(metaClass);
            }

            return metaClass;
        }
        return getExpandoMetaClass(instance.getClass());
    }

    private static MetaClass unwrapDelegatingMetaClass(MetaClass metaClass) {
        int counter = 0;
        while (metaClass instanceof DelegatingMetaClass && counter++ < MAX_DELEGATE_LEVELS) {
            metaClass = ((DelegatingMetaClass) metaClass).getAdaptee();
        }
        return metaClass;
    }

    /**
     * Obtains a property of an instance if it exists
     *
     * @param instance The instance
     * @param property The property
     * @return The value of null if non-exists
     */
    public static Object getPropertyIfExists(Object instance, String property) {
        return getPropertyIfExists(instance, property, Object.class);
    }

    /**
     * Obtains a property of an instance if it exists
     *
     * @param instance The instance
     * @param property The property
     * @param requiredType The required type of the property
     * @return The property value
     */
    @SuppressWarnings("unchecked")
    public static  T getPropertyIfExists(Object instance, String property, Class requiredType) {
        MetaClass metaClass = getMetaClass(instance);
        MetaProperty metaProperty = metaClass.getMetaProperty(property);

        if (metaProperty != null) {
            Object value = metaProperty.getProperty(instance);
            if (value != null && requiredType.isInstance(value)) {
                return (T) value;
            }
        }
        return null;
    }

    /**
     * Invokes a method if it exists otherwise returns null
     *
     * @param instance The instance
     * @param methodName The method name
     * @return The result of the method call or null
     */
    public static Object invokeMethodIfExists(Object instance, String methodName) {
        return invokeMethodIfExists(instance, methodName, NO_ARGS);
    }

    /**
     * Invokes a method if it exists otherwise returns null
     *
     * @param instance The instance
     * @param methodName The method name
     * @param args The arguments
     *
     * @return The result of the method call or null
     */
    public static Object invokeMethodIfExists(Object instance, String methodName, Object[] args) {
        MetaClass metaClass = getMetaClass(instance);
        List methodList = metaClass.respondsTo(instance, methodName, args);
        if (methodList != null && !methodList.isEmpty()) {
            return metaClass.invokeMethod(instance, methodName, args);
        }
        return null;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy