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

org.boon.core.reflection.Reflection Maven / Gradle / Ivy

Go to download

Simple opinionated Java for the novice to expert level Java Programmer. Low Ceremony. High Productivity. A real boon to Java to developers!

There is a newer version: 0.34
Show newest version
/*
 * Copyright 2013-2014 Richard M. Hightower
 * 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.
 *
 * __________                              _____          __   .__
 * \______   \ ____   ____   ____   /\    /     \ _____  |  | _|__| ____    ____
 *  |    |  _//  _ \ /  _ \ /    \  \/   /  \ /  \\__  \ |  |/ /  |/    \  / ___\
 *  |    |   (  <_> |  <_> )   |  \ /\  /    Y    \/ __ \|    <|  |   |  \/ /_/  >
 *  |______  /\____/ \____/|___|  / \/  \____|__  (____  /__|_ \__|___|  /\___  /
 *         \/                   \/              \/     \/     \/       \//_____/
 *      ____.                     ___________   _____    ______________.___.
 *     |    |____ ___  _______    \_   _____/  /  _  \  /   _____/\__  |   |
 *     |    \__  \\  \/ /\__  \    |    __)_  /  /_\  \ \_____  \  /   |   |
 * /\__|    |/ __ \\   /  / __ \_  |        \/    |    \/        \ \____   |
 * \________(____  /\_/  (____  / /_______  /\____|__  /_______  / / ______|
 *               \/           \/          \/         \/        \/  \/
 */

package org.boon.core.reflection;

import org.boon.Exceptions;
import org.boon.Lists;
import org.boon.core.Function;
import org.boon.Pair;
import org.boon.core.Sys;
import org.boon.core.Typ;
import org.boon.core.reflection.fields.FieldAccess;
import org.boon.core.reflection.fields.PropertyField;
import org.boon.core.reflection.fields.ReflectField;
import org.boon.core.reflection.fields.UnsafeField;
import sun.misc.Unsafe;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;

import static org.boon.Boon.sputs;
import static org.boon.Exceptions.die;
import static org.boon.Str.lower;
import static org.boon.Str.slc;


public class Reflection {



    public static List getFields( Class theClass ) {


        List fields = context().__fields.get(theClass);
        if (fields!=null) {
            return fields;
        }

        fields = Lists.list( theClass.getDeclaredFields() );
        boolean foundCrap = false;
        for ( Field field : fields ) {
            if (field.getName().indexOf('$')!=-1) {
                foundCrap = true;
                continue;
            }
            field.setAccessible( true );
        }


        if (foundCrap) {
            List copy = Lists.copy(fields);

            for (Field field : copy) {
                if (field.getName().indexOf('$')!=-1) {
                   fields.remove(field);
                }


            }
        }

        return fields;
    }


    private static final Logger log = Logger.getLogger( Reflection.class.getName() );

    private static boolean _useUnsafe;

    static {
        try {
            Class.forName( "sun.misc.Unsafe" );
            _useUnsafe = true;
        } catch ( ClassNotFoundException e ) {
            e.printStackTrace();
            _useUnsafe = false;
        }

        _useUnsafe = _useUnsafe && !Boolean.getBoolean( "org.boon.noUnsafe" );
    }

    private static final boolean useUnsafe = _useUnsafe;



    private final static Context _context;
    private static WeakReference weakContext = new WeakReference<>( null );


    static {

        boolean noStatics = Boolean.getBoolean( "org.boon.noStatics" );
        if ( noStatics || Sys.inContainer() ) {

            _context = null;
            weakContext = new WeakReference<>( new Context() );

        } else {
            ;
            _context = new Context();
        }
    }




    public static Unsafe getUnsafe() {
        if ( context().control == null ) {
            try {
                Field f = Unsafe.class.getDeclaredField( "theUnsafe" );
                f.setAccessible( true );
                context().control = ( Unsafe ) f.get( null );
                return context().control;
            } catch ( Exception e ) {
                return null;
            }
        } else {
            return context().control;
        }
    }


    public static Object contextToHold() {
        return context();
    }

    /* Manages weak references. */
    static Context context() {

        if ( _context != null ) {
            return _context;
        } else {
            Context context = weakContext.get();
            if ( context == null ) {
                context = new Context();
                weakContext = new WeakReference<>( context );
            }
            return context;
        }
    }

    static class Context {


        Map, List> __fields = new ConcurrentHashMap<>( 200 );

        Unsafe control;
        Map _sortableFields = new ConcurrentHashMap<>();

        Map, ClassMeta> _classMetaMap = new ConcurrentHashMap<>( 200 );

        Map, Map> _allAccessorReflectionFieldsCache = new ConcurrentHashMap<>( 200 );
        Map, Map> _allAccessorPropertyFieldsCache = new ConcurrentHashMap<>( 200 );
        Map, Map> _allAccessorUnsafeFieldsCache = new ConcurrentHashMap<>( 200 );

        Map, Map> _combinedFieldsFieldsFirst = new ConcurrentHashMap<>( 200 );
        Map, Map> _combinedFieldsFieldsFirstForSerializer = new ConcurrentHashMap<>( 200 );

        Map, Map> _combinedFieldsPropertyFirst = new ConcurrentHashMap<>( 200 );
        Map, Map> _combinedFieldsPropertyFirstForSerializer = new ConcurrentHashMap<>( 200 );

    }



    private static Map getCombinedFieldsPropertyFirst(Class theClass) {
        return context()._combinedFieldsPropertyFirst.get(theClass);
    }


    private static Map getCombinedFieldsPropertyFirstForSerializer(Class theClass) {
        return context()._combinedFieldsPropertyFirstForSerializer.get(theClass);
    }

    private static Map getCombinedFieldsFieldFirst(Class theClass) {
        return context()._combinedFieldsFieldsFirst.get(theClass);
    }

    private static Map getCombinedFieldsFieldFirstForSerializer(Class theClass) {
        return context()._combinedFieldsFieldsFirstForSerializer.get(theClass);
    }

    private static void  putCombinedFieldsPropertyFirst(Class theClass, Map map) {
        context()._combinedFieldsPropertyFirst.put(theClass, map);
    }


    private static void  putCombinedFieldsPropertyFirstForSerializer(Class theClass, Map map) {
        context()._combinedFieldsPropertyFirst.put(theClass, map);
    }

    private static void putCombinedFieldsFieldFirst(Class theClass,  Map map) {
        context()._combinedFieldsFieldsFirst.put(theClass, map);
    }

    private static void putCombinedFieldsFieldFirstForSerializer(Class theClass,  Map map) {
        context()._combinedFieldsFieldsFirstForSerializer.put(theClass, map);
    }

    static {
        try {
            if ( _useUnsafe ) {
                Field field = String.class.getDeclaredField( "value" );
            }
        } catch ( Exception ex ) {
            Exceptions.handle( ex );
        }
    }


    private static void setAccessorFieldInCache( Class theClass, boolean useUnsafe, Map map ) {
        if ( useUnsafe ) {
            context()._allAccessorUnsafeFieldsCache.put( theClass, map );
        } else {
            context()._allAccessorReflectionFieldsCache.put( theClass, map );

        }
    }

    private static void setPropertyAccessorFieldsInCache( Class theClass, Map map ) {
        context()._allAccessorPropertyFieldsCache.put( theClass, map );
    }


    private static Map getPropertyAccessorFieldsFromCache( Class theClass ) {
        return context()._allAccessorPropertyFieldsCache.get( theClass );
    }

    private static Map getAccessorFieldsFromCache(Class theClass, boolean useUnsafe) {

        if ( useUnsafe ) {
            return context()._allAccessorUnsafeFieldsCache.get( theClass );
        } else {
            return context()._allAccessorReflectionFieldsCache.get( theClass );

        }
    }




    /**
     * Gets a list of fields merges with properties if field is not found.
     *
     * @param clazz get the properties or fields
     * @return
     */
    public static Map getPropertyFieldAccessMapFieldFirst( Class clazz ) {
        Map combinedFieldsFieldFirst = getCombinedFieldsFieldFirst(clazz);

        if (combinedFieldsFieldFirst!=null) {
            return combinedFieldsFieldFirst;
        } else {

            /* Fallback map. */
            Map fieldsFallbacks = null;

            /* Primary merge into this one. */
            Map fieldsPrimary = null;


             /* Try to find the fields first if this is set. */
            fieldsPrimary = Reflection.getAllAccessorFields(clazz, true);

            fieldsFallbacks = Reflection.getPropertyFieldAccessors(clazz);


            combineFieldMaps(fieldsFallbacks, fieldsPrimary);

            combinedFieldsFieldFirst = fieldsPrimary;

            putCombinedFieldsFieldFirst(clazz, combinedFieldsFieldFirst);
            return combinedFieldsFieldFirst;

        }


    }



    /**
     * Gets a list of fields merges with properties if field is not found.
     *
     * @param clazz get the properties or fields
     * @return
     */
    public static Map getPropertyFieldAccessMapFieldFirstForSerializer( Class clazz ) {
        Map combinedFieldsFieldFirst = getCombinedFieldsFieldFirstForSerializer(clazz);

        if (combinedFieldsFieldFirst!=null) {
            return combinedFieldsFieldFirst;
        } else {

            /* Fallback map. */
            Map fieldsFallbacks = null;

            /* Primary merge into this one. */
            Map fieldsPrimary = null;


             /* Try to find the fields first if this is set. */
            fieldsPrimary = Reflection.getAllAccessorFields(clazz, true);
            fieldsFallbacks = Reflection.getPropertyFieldAccessors(clazz);

            fieldsPrimary = removeNonSerializable(fieldsPrimary);
            fieldsFallbacks = removeNonSerializable(fieldsFallbacks);

            combineFieldMaps(fieldsFallbacks, fieldsPrimary);

            combinedFieldsFieldFirst = fieldsPrimary;

            putCombinedFieldsFieldFirstForSerializer(clazz, combinedFieldsFieldFirst);
            return combinedFieldsFieldFirst;

        }


    }


    private static void combineFieldMaps( Map fieldsFallbacks, Map fieldsPrimary ) {
    /* Add missing fields */
        for ( Map.Entry field : fieldsFallbacks.entrySet() ) {
            if ( !fieldsPrimary.containsKey( field.getKey() ) ) {
                fieldsPrimary.put( field.getKey(), field.getValue() );
            }
        }
    }

    public static Map getPropertyFieldAccessMapPropertyFirst( Class clazz ) {

        Map combinedFields = getCombinedFieldsPropertyFirst(clazz);

        if (combinedFields!=null) {
            return combinedFields;
        } else {
             /* Fallback map. */
            Map fieldsFallbacks = null;

            /* Primary merge into this one. */
            Map fieldsPrimary = null;



             /* Try to find the properties first if this is set. */
            fieldsFallbacks = Reflection.getAllAccessorFields(clazz, true);
            fieldsPrimary = Reflection.getPropertyFieldAccessors(clazz);


            /* Add missing fields */
            combineFieldMaps(fieldsFallbacks, fieldsPrimary);

            combinedFields = fieldsPrimary;
            putCombinedFieldsPropertyFirst(clazz, combinedFields);
            return combinedFields;
        }
    }


    public static Map getPropertyFieldAccessMapPropertyFirstForSerializer( Class clazz ) {

        Map combinedFields = getCombinedFieldsPropertyFirstForSerializer(clazz);

        if (combinedFields!=null) {
            return combinedFields;
        } else {
             /* Fallback map. */
            Map fieldsFallbacks = null;

            /* Primary merge into this one. */
            Map fieldsPrimary = null;



             /* Try to find the properties first if this is set. */
            fieldsFallbacks = Reflection.getAllAccessorFields(clazz, true);
            fieldsFallbacks = removeNonSerializable(fieldsFallbacks);

            fieldsPrimary = Reflection.getPropertyFieldAccessors(clazz);
            fieldsPrimary = removeNonSerializable(fieldsPrimary);

            /* Add missing fields */
            combineFieldMaps(fieldsFallbacks, fieldsPrimary);

            combinedFields = fieldsPrimary;
            putCombinedFieldsPropertyFirstForSerializer(clazz, combinedFields);
            return combinedFields;
        }
    }

    private static Map removeNonSerializable(Map fieldAccessMap) {

        LinkedHashMap map = new LinkedHashMap<>(fieldAccessMap);
        final List set = new ArrayList(fieldAccessMap.keySet());
        for (String key : set) {
            final FieldAccess fieldAccess = fieldAccessMap.get(key);
            if (fieldAccess.isStatic() || fieldAccess.ignore() ) {
                map.remove(key);
            }
        }
        return map;
    }

    @SuppressWarnings ( "serial" )
    public static class ReflectionException extends RuntimeException {

        public ReflectionException() {
            super();
        }

        public ReflectionException( String message, Throwable cause ) {
            super( message, cause );
        }

        public ReflectionException( String message ) {
            super( message );
        }

        public ReflectionException( Throwable cause ) {
            super( cause );
        }
    }


    private static void handle( Exception ex ) {
        throw new ReflectionException( ex );
    }





    public static Class loadClass( String className ) {

        try {
            Class clazz = Class.forName( className );


            return clazz;


        } catch ( Exception ex ) {
            log.info( String.format( "Unable to create load class %s", className ) );
            return null;
        }
    }
    public static Object newInstance( String className ) {

        try {
            Class clazz = Class.forName( className );


            return newInstance( clazz );


        } catch ( Exception ex ) {
            log.info( String.format( "Unable to create this class %s", className ) );
            return null;
        }
    }

    public static  T newInstance( Class clazz ) {
        T newInstance = null;
        ClassMeta  cls = ClassMeta.classMeta(clazz);

        try {
            /* See if there is a no arg constructor. */
            ConstructorAccess declaredConstructor = cls.noArgConstructor();
            if (declaredConstructor !=null ) {
                /* If there was a no argument constructor, then use it. */
                newInstance = declaredConstructor.create();
            } else {
                if ( _useUnsafe ) {
                    newInstance = ( T ) getUnsafe().allocateInstance( clazz );
                } else {
                    die ( sputs( clazz.getName (), "does not have a no arg constructor and unsafe is not turned on" ) );
                }

            }
        } catch ( Exception ex ) {
            try {
                if ( _useUnsafe ) {
                    newInstance = ( T ) getUnsafe().allocateInstance( clazz );
                    return newInstance; //we handled it.
                }
            } catch ( Exception ex2 ) {
                handle( ex2 );
            }

            handle( ex );
        }

        return newInstance;

    }

    public static  T newInstance( Class clazz, Object arg ) {
        T newInstance = null;


        ClassMeta  cls = ClassMeta.classMeta(clazz);
         try {
            /* See if there is a no arg constructor. */
            ConstructorAccess declaredConstructor = cls.declaredConstructor(arg.getClass());
            if (declaredConstructor !=null ) {
                /* If there was a no argument constructor, then use it. */
                newInstance = declaredConstructor.create(arg);
            }
        } catch ( Exception ex ) {
            handle( ex );
        }
        return newInstance;
    }

    public static Class getComponentType( Collection collection, FieldAccess fieldAccess ) {
        Class clz = fieldAccess.getComponentClass();
        if ( clz == null ) {
            clz = getComponentType( collection );
        }
        return clz;

    }

    public static Class getComponentType( Collection value ) {
        if ( value.size() > 0 ) {
            Object next = value.iterator().next();
            return next.getClass();
        } else {
            return Typ.object;
        }
    }

    private static class FieldConverter implements Function {

        boolean thisUseUnsafe;

        FieldConverter( boolean useUnsafe ) {
            this.thisUseUnsafe = useUnsafe;
        }

        @Override
        public FieldAccess apply( Field from ) {
            if ( useUnsafe && thisUseUnsafe ) {
                return UnsafeField.createUnsafeField( from );
            } else {
                return new ReflectField( from );
            }
        }
    }

    public static Map getAllAccessorFields(
            Class theClass ) {
        return getAllAccessorFields( theClass, true );
    }

    public static Map getAllAccessorFields(
            Class theClass, boolean useUnsafe ) {
        Map map = getAccessorFieldsFromCache(theClass, useUnsafe);
        if ( map == null ) {
            List list = Lists.mapBy( getAllFields( theClass ), new FieldConverter( useUnsafe ) );
            map = new LinkedHashMap<>( list.size() );
            for ( FieldAccess fieldAccess : list ) {
                map.put( fieldAccess.name(), fieldAccess );
            }
            setAccessorFieldInCache( theClass, useUnsafe, map );
        }
        return map;
    }


    public static List getAllFields( Class theClass ) {

        try {
            List list = getFields( theClass );
            while ( theClass != Typ.object ) {

                theClass = theClass.getSuperclass();
                getFields( theClass, list );
            }
            return list;
        } catch (Exception ex) {
            return  Exceptions.handle(List.class, ex, "getAllFields the class", theClass);
        }
    }


    public static Map getPropertyFieldAccessors(
            Class theClass ) {


        Map fields = getPropertyAccessorFieldsFromCache( theClass );
        if ( fields == null ) {
            Map> methods = getPropertySetterGetterMethods( theClass );

            fields = new LinkedHashMap<>();

            for ( Map.Entry> entry :
                    methods.entrySet() ) {

                final Pair methodPair = entry.getValue();
                final String key = entry.getKey();

                PropertyField pf = new PropertyField( key, methodPair.getFirst(), methodPair.getSecond() );

                fields.put( pf.alias(), pf );

            }

            setPropertyAccessorFieldsInCache( theClass, fields );
        }


        return fields;
    }

    public static Map> getPropertySetterGetterMethods(
            Class theClass ) {

        try {
            Method[] methods = theClass.getMethods();

            Map> methodMap = new LinkedHashMap<>( methods.length );
            List getterMethodList = new ArrayList<>( methods.length );

            for ( int index = 0; index < methods.length; index++ ) {
                Method method = methods[ index ];
                if (extractPropertyInfoFromMethodPair(methodMap, getterMethodList, method)) continue;
            }

            for ( Method method : getterMethodList ) {
                extractProperty(methodMap, method);

            }
            return methodMap;
        } catch (Exception ex) {
            ex.printStackTrace();
            return Exceptions.handle(Map.class, ex, theClass);
        }
    }

    private static boolean extractPropertyInfoFromMethodPair(Map> methodMap,
                                                             List getterMethodList,
                                                             Method method) {
        String name = method.getName();

        try {

            if ( method.getParameterTypes().length == 1
                    && name.startsWith( "set" ) ) {
                Pair pair = new Pair<>();
                pair.setFirst( method );
                String propertyName = slc( name, 3 );

                propertyName = lower( slc( propertyName, 0, 1 ) ) + slc( propertyName, 1 );
                methodMap.put( propertyName, pair );
            }

            if ( method.getParameterTypes().length > 0
                    || method.getReturnType() == void.class
                    || !( name.startsWith( "get" ) || name.startsWith( "is" ) )
                    || name.equals( "getClass" ) || name.equals("get") || name.equals("is") ) {
                return true;
            }
            getterMethodList.add( method );
            return false;

        } catch (Exception ex) {
            return Exceptions.handle(Boolean.class, ex, name, method);
        }
    }

    private static void extractProperty(Map> methodMap,
                                        Method method) {
        try {
            String name = method.getName();
            String propertyName = null;
            if ( name.startsWith( "is" ) ) {
                propertyName = name.substring( 2 );
            } else if ( name.startsWith( "get" ) ) {
                propertyName = name.substring( 3 );
            }

            propertyName = lower( propertyName.substring( 0, 1 ) ) + propertyName.substring( 1 );

            Pair pair = methodMap.get( propertyName );
            if ( pair == null ) {
                pair = new Pair<>();
                methodMap.put( propertyName, pair );
            }
            pair.setSecond(method);

        } catch (Exception ex) {
            Exceptions.handle(ex, "extractProperty property extract of getPropertySetterGetterMethods", method);
        }
    }

    public static void getFields( Class theClass,
                                  List list ) {

        try {
            List more = getFields( theClass );
            list.addAll( more );

        }catch (Exception ex) {
            Exceptions.handle(ex, "getFields", theClass, list);
        }
    }


    public static boolean respondsTo( Class type, String methodName) {
        return ClassMeta.classMeta(type).respondsTo(methodName);
    }

    public static boolean respondsTo( Class type, String methodName, Class... params) {
        return ClassMeta.classMeta(type).respondsTo(methodName, params);
    }


    public static boolean respondsTo( Class type, String methodName, Object... params) {
        return ClassMeta.classMeta(type).respondsTo(methodName, params);
    }


    public static boolean respondsTo( Class type, String methodName, List params) {
        return ClassMeta.classMeta(type).respondsTo(methodName, params);
    }


    public static boolean respondsTo( Object object, String methodName) {
        if (object == null || methodName == null) {
            return false;
        }
        return ClassMeta.classMeta(object.getClass()).respondsTo(methodName);
    }

    public static boolean respondsTo( Object object, String methodName, Class... params) {
        return ClassMeta.classMeta(object.getClass()).respondsTo(methodName, params);
    }


    public static boolean respondsTo( Object object, String methodName, Object... params) {
        return ClassMeta.classMeta(object.getClass()).respondsTo(methodName, params);
    }


    public static boolean respondsTo( Object object, String methodName, List params) {
        return ClassMeta.classMeta(object.getClass()).respondsTo(methodName, params);
    }


    public static boolean handles( Object object, Class interfaceCls) {
        return ClassMeta.classMeta(object.getClass()).handles(interfaceCls);
    }


    public static boolean handles( Class cls, Class interfaceCls) {
        return ClassMeta.classMeta(cls).handles(interfaceCls);
    }


    public static Object invoke (Object object, String name, Object... args){
        return ClassMeta.classMeta( object.getClass() ).invokeUntyped(object, name, args );
    }


    public static Object invoke (Object object, String name, List args){
        return ClassMeta.classMeta( object.getClass() ).invokeUntyped(object, name, args.toArray(new Object[args.size()]));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy