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

net.sf.juffrou.util.reflect.ReflectionUtil Maven / Gradle / Ivy

Go to download

Performant java bean access through property names. If you are serious about bean handling, this is for you.

There is a newer version: 2.1.9
Show newest version
package net.sf.juffrou.util.reflect;

import java.lang.reflect.Array;
import java.lang.reflect.GenericArrayType;
import java.lang.reflect.GenericDeclaration;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.TypeVariable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Utility methods to provide information over generic types.

* When a generic class is extended we often need to know which types were used to typify the generic. * @author cemartins */ public final class ReflectionUtil { /** * Get the underlying class for a type, or null if the type is a variable type. * * @param type * the type * @return the underlying class */ public static Class getClass(Type type) { if (type instanceof Class) { return (Class) type; } else if (type instanceof ParameterizedType) { return getClass(((ParameterizedType) type).getRawType()); } else if (type instanceof GenericArrayType) { Type componentType = ((GenericArrayType) type).getGenericComponentType(); Class componentClass = getClass(componentType); if (componentClass != null) { return Array.newInstance(componentClass, 0).getClass(); } else { return null; } } else { if(type instanceof TypeVariable) { TypeVariable typeVariable = (TypeVariable) type; GenericDeclaration genericDeclaration = typeVariable.getGenericDeclaration(); return type.getClass(); } else { return null; } } } /** * Get the actual type arguments a child class has used to extend a generic base class. * * @param baseClass * the base class * @param childClass * the child class * @return a list of the raw classes for the actual type arguments. */ public static List> getTypeArguments(Class baseClass, Class childClass) { Map resolvedTypes = new HashMap(); Type type = childClass; // start walking up the inheritance hierarchy until we hit baseClass while (type != null && !getClass(type).equals(baseClass)) { if (type instanceof Class) { // there is no useful information for us in raw types, so just keep going. type = ((Class) type).getGenericSuperclass(); } else { ParameterizedType parameterizedType = (ParameterizedType) type; Class rawType = (Class) parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); TypeVariable[] typeParameters = rawType.getTypeParameters(); for (int i = 0; i < actualTypeArguments.length; i++) { resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); } if (!rawType.equals(baseClass)) { type = rawType.getGenericSuperclass(); } } } // finally, for each actual type argument provided to baseClass, determine (if possible) // the raw class for that type argument. Type[] actualTypeArguments; if (type instanceof Class) { actualTypeArguments = ((Class) type).getTypeParameters(); } else { if(type != null) { actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments(); } else { actualTypeArguments = null; } } List> typeArgumentsAsClasses = new ArrayList>(); if(actualTypeArguments == null || actualTypeArguments.length == 0) { for(Type baseType : resolvedTypes.values()) { typeArgumentsAsClasses.add(getClass(baseType)); } } else { // resolve types by chasing down type variables. for (Type baseType : actualTypeArguments) { while (resolvedTypes.containsKey(baseType)) { baseType = resolvedTypes.get(baseType); } typeArgumentsAsClasses.add(getClass(baseType)); } } return typeArgumentsAsClasses; } /** * Get the type parameters and the corresponding actual type arguments a child class has used to extend a generic base class. * * @param baseClass * the base class * @param childClass * the child class * @return A map where the keys are TypeVariable (the type variables declared by the generic declaration) and the values are the Type objects representing the actual type argument the corresponds. */ public static Map, Type> getTypeArgumentsMap(Class baseClass, Class childClass) { Map, Type> resolvedTypes = new HashMap, Type>(); Type type = childClass; // start walking up the inheritance hierarchy until we hit baseClass while (type != null && !getClass(type).equals(baseClass)) { if (type instanceof Class) { // there is no useful information for us in raw types, so just keep going. type = ((Class) type).getGenericSuperclass(); } else { ParameterizedType parameterizedType = (ParameterizedType) type; Class rawType = (Class) parameterizedType.getRawType(); Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); TypeVariable[] typeParameters = rawType.getTypeParameters(); for (int i = 0; i < actualTypeArguments.length; i++) { resolvedTypes.put(typeParameters[i], actualTypeArguments[i]); } if (!rawType.equals(baseClass)) { type = rawType.getGenericSuperclass(); } } } return resolvedTypes; } /** * Checks whether a type is a simple java type.

* Simple java types are primitives, classes included in the "java" package, Interfaces and Enumerations. * Simple java types cannot be java beans, so if this method returns true, you cannot create a BeanWrapper * around the specified type. * @param type the type to test * @return true if is a simple java type (is not a bean); false if it is not a simple type (may still not be a bean). */ public static boolean isSimpleType(Type type) { if(! (type instanceof Class)) return false; Class clazz = (Class) type; return clazz.isPrimitive() || clazz.isEnum() || clazz.isInterface() || clazz.getName().startsWith("java"); } /** * Transform a Java bean into a Map where the keys are the property names.

* If there are nested beans, then the key will be the path of property names in the form "prop1.prop2.prop2".
* Properties with null values are not put in the map. * @param bean The bean to transform * @return a Map with keys and values corresponding to the bean. * @see {@link #getMapFromBean(BeanWrapperFactory factory, Object bean)} for increased performance. */ public static Map getMapFromBean(Object bean) { return getMapFromBean(new BeanWrapperFactory(), bean); } /** * Transform a Java bean into a Map where the keys are the property names.

* If there are nested beans, then the key will be the path of property names in the form "prop1.prop2.prop2".
* Properties with null values are not put in the map. * @param factory a BeanWrapperFactory instance to cache introspection information. Increases performance if re-used. * @param bean The bean to transform * @return a Map with keys and values corresponding to the bean. * @see {@link #getBeanFromMap(BeanWrapperFactory, Map, Object)} */ public static Map getMapFromBean(BeanWrapperFactory factory, Object bean) { Map beanMap = new HashMap(); Map circularReferences = new HashMap(); BeanWrapper beanWrapper = factory.getBeanWrapper(bean); circularReferences.put(bean, ""); buildMapFromBean(factory, beanWrapper, circularReferences, "",beanMap); return beanMap; } private static void buildMapFromBean(BeanWrapperFactory factory, BeanWrapper beanWrapper, Map circularReferences, String pathPrefix, Map beanMap) { for(String propertyName : beanWrapper.getPropertyNames()) { Object value = beanWrapper.getValue(propertyName); if(value == null) continue; if(isSimpleType(beanWrapper.getType(propertyName))) beanMap.put(pathPrefix + propertyName, value); else if(circularReferences.containsKey(value)) beanMap.put(pathPrefix + propertyName, new CircularReference(circularReferences.get(value))); else { circularReferences.put(value, pathPrefix + propertyName); buildMapFromBean(factory, beanWrapper.getNestedWrapper(propertyName), circularReferences, pathPrefix + propertyName + ".", beanMap); } } } /** * Fill up a java bean with the contents of a map where the keys are property names. * @param beanMap Map with property names and values * @param bean bean instance to fill up * @see {@link #getBeanFromMap(BeanWrapperFactory, Map, Object)} for increased performance. */ public static void getBeanFromMap(Map beanMap, Object bean) { getBeanFromMap(new BeanWrapperFactory(), beanMap, bean); } /** * Fill up a java bean with the contents of a map where the keys are property names. * @param factory a BeanWrapperFactory instance to cache introspection information. Increases performance if re-used. * @param beanMap Map with property names and values * @param bean bean instance to fill up */ public static void getBeanFromMap(BeanWrapperFactory factory, Map beanMap, Object bean) { Map circularReferences = new HashMap(); BeanWrapper beanWrapper = factory.getBeanWrapper(bean); for(Entry entry : beanMap.entrySet()) if(entry.getValue() instanceof CircularReference) circularReferences.put(entry.getKey(), (CircularReference) entry.getValue()); else beanWrapper.setValue(entry.getKey(), entry.getValue()); // set the circular references after the other properties so that they can reference existing values for(Entry entry : circularReferences.entrySet()) beanWrapper.setValue(entry.getKey(), entry.getValue().getPath().isEmpty() ? beanWrapper.getBean() : beanWrapper.getValue(entry.getValue().getPath())); } private static class CircularReference { private final String path; private CircularReference(String path) { this.path = path; } public String getPath() { return path; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy