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

net.sf.juffrou.util.reflect.BeanWrapper 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.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.juffrou.error.ReflectionException;
import net.sf.juffrou.util.reflect.internal.BeanFieldHandler;



/**
 * Bean handling through property names.

* Allows access to the wrapped bean's properties and also to the properties of beans referenced by them. For example * the property path "pro1.prop2" will access the property prop2 from the nested bean referenced by prop1.
* This bean wrapper auto grows nested paths, so for each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1. The nested bean wrappers can be * obtained by calling the getNestesWrappers() method.
* You can reference nested properties as deep as you like as long as the bean path exists. * * @author cemartins */ public class BeanWrapper { private final BeanWrapperContext context; private final BeanWrapper parentBeanWrapper; private final String parentBeanProperty; private Object wrappedInstance; private final Map nestedWrappers = new HashMap(); /** * Construct a bean wrapper using the metadata and preferences of an existing BeanWrapperContext.
* Performance is better, because there is no need to do introspection. * @param context metadata information about the class to instantiate the wrapped bean */ public BeanWrapper(BeanWrapperContext context) { this.context = context; this.wrappedInstance = null; this.parentBeanWrapper = null; this.parentBeanProperty = null; } /** * Construct a bean wrapper using the metadata and preferences of an existing BeanWrapperContext and initializes it with an instance value.
* Performance is better, because there is no need to do introspection. * @param context metadata and preferences information about the class * @param instance bean instance */ public BeanWrapper(BeanWrapperContext context, Object instance) { this.context = context; this.parentBeanWrapper = null; this.parentBeanProperty = null; setBean(instance); } public BeanWrapper(BeanWrapperContext context, BeanWrapper parentBeanWrapper, String parentBeanProperty) { this.context = context; this.wrappedInstance = null; this.parentBeanWrapper = parentBeanWrapper; this.parentBeanProperty = parentBeanProperty; } /** * Construct a bean wrapper around an existing bean instance.
* This constructor will have to create a BeanWrapperContext to get introspection metadata. You can use {@link #BeanWrapper(BeanWrapperContext, Object)} instead. * @param instance */ public BeanWrapper(Object instance) { this.wrappedInstance = instance; this.context = BeanWrapperContext.create(instance.getClass()); this.parentBeanWrapper = null; this.parentBeanProperty = null; } /** * Construct a bean wrapper around a class. Bean instances will be instances of that class.
* This constructor will have to create a BeanWrapperContext to get introspection metadata. You can use {@link #BeanWrapper(BeanWrapperContext, Object)} instead. * @param clazz class to instantiate the wrapped bean */ public BeanWrapper(Class clazz) { this.context = BeanWrapperContext.create(clazz); this.wrappedInstance = null; this.parentBeanWrapper = null; this.parentBeanProperty = null; } private boolean isRoot() { return (this.parentBeanWrapper == null); } /** * @return the BeanWrapperContext containing introspection information about this bean */ public BeanWrapperContext getContext() { return context; } /** * @return The BeanWrapperFactory responsible for creating and caching the BeanWrapperContext for this bean and all its nested beans. */ public BeanWrapperFactory getFactory() { return context.getFactory(); } /** * Get the wrapped bean * * @return the wrapped bean */ public Object getBean() { if(isRoot()) { if(wrappedInstance == null) createInstance(); return wrappedInstance; } return parentBeanWrapper.getContext().getBeanFieldHandler(parentBeanProperty).getValue(parentBeanWrapper); } /** * Replaces the wrapped bean with another instance of the same type * @param bean instance of the new bean to wrap * @throws InvalidParameterException if the new bean is not of the same type of the initially wrapped bean. */ public void setBean(Object bean) { if(bean != null && ! context.getBeanClass().isAssignableFrom(bean.getClass())) throw new InvalidParameterException("Bean must be of type " + context.getBeanClass().getSimpleName()); if(isRoot()) wrappedInstance = bean; else parentBeanWrapper.setValue(parentBeanProperty, bean); } /** * Get the wrapped bean class * @return */ public Class getBeanClass() { return context.getBeanClass(); } /** * Returns all the nested bean wrappers that have been created inside this bean wrapper.
* Nested bean wrappers are created when you access a nested property (i.e. getValue("prop1.prop2")) * @return a Map where the keys are property names and the values are bean wrappers */ public Map getNestedWrappers() { return nestedWrappers; } /** * Checks whether a property exists in the wrapped bean. If that property references another bean (a nested bean) * the method verifies if the property of the nested bean also exists.
* For example hasProperty("pro1.prop2") returns true only if prop1 exists is this bean and prop2 exists in the bean referenced by prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* @param propertyName * @return */ public boolean hasProperty(String propertyName) { int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { // not a nested property return context.getFields().containsKey(propertyName); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); return nestedWrapper.hasProperty(nestedProperty); } } /** * Get the names of all properties found in this bean and base ascending hierarchy * @return unordered list of property names. */ public String[] getPropertyNames() { List propertyNames = new ArrayList(); propertyNames.addAll(context.getFields().keySet()); return propertyNames.toArray(new String[0]); } /** * Returns the string representation of the wrapped bean instance, or and empty string if the instance is null. * @see java.lang.Object#toString() */ @Override public String toString() { Object instance = getBean(); return instance == null ? "" : instance.toString(); } /** * Gets the value of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property values can also be obtained by specifying a property path.
* For example getValue("pro1.prop2") will get the value of prop2 from the nested bean referenced by * prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @return the value held in the bean property */ public Object getValue(String propertyName) { if(getBean() == null) return null; int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { return context.getBeanFieldHandler(propertyName).getValue(this); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); return nestedWrapper.getValue(nestedProperty); } } /** * Gets the class of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property types can also be obtained by specifying a property path.
* For example getType("pro1.prop2") will get the type of prop2 from the nested bean referenced by * prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @return */ public Class getClazz(String propertyName) { return ReflectionUtil.getClass(getType(propertyName)); } /** * Gets the type of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property types can also be obtained by specifying a property path.
* For example getType("pro1.prop2") will get the type of prop2 from the nested bean referenced by * prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @return */ public Type getType(String propertyName) { int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { Object value = getBean() == null ? null : context.getBeanFieldHandler(propertyName).getValue(this); if(value != null) return value.getClass(); else return context.getBeanFieldHandler(propertyName).getType(); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); Object value = context.getBeanFieldHandler(thisProperty).getValue(this); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapperContext nestedContext = context.getNestedContext(thisProperty, value); return nestedContext.getType(nestedProperty); } } /** * Gets the type of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property types can also be obtained by specifying a property path.
* For example getType("pro1.prop2") will get the type of prop2 from the nested bean referenced by * prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @return */ public Type[] getTypeArguments(String propertyName) { int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { return context.getBeanFieldHandler(propertyName).getTypeArguments(); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); return nestedWrapper.getTypeArguments(nestedProperty); } } /** * Gets the type of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property types can also be obtained by specifying a property path.
* For example getType("pro1.prop2") will get the type of prop2 from the nested bean referenced by * prop1.
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @return */ public Field getField(String propertyName) { int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { return context.getBeanFieldHandler(propertyName).getField(); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); return nestedWrapper.getField(nestedProperty); } } /** * Same as setValue(String propertyName, Object value) but the value will be converted from String to * whatever type the property referenced by propertyName is. * * @param propertyName * @param value * String representation of the value to be set */ public void setValueOfString(String propertyName, String value) { if(getBean() == null) createInstance(); int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { // not a nested property BeanFieldHandler beanFieldHandler = context.getBeanFieldHandler(propertyName); Class paramType = (Class) beanFieldHandler.getType(); try { if (paramType.equals(String.class)) { beanFieldHandler.setValue(this, value); } else { Constructor constructor = paramType.getConstructor(new Class[] { String.class }); Object convertedValue = constructor.newInstance(value); beanFieldHandler.setValue(this, convertedValue); } } catch (IllegalArgumentException e) { throw new ReflectionException(e); } catch (IllegalAccessException e) { throw new ReflectionException(e); } catch (InvocationTargetException e) { throw new ReflectionException(e); } catch (SecurityException e) { throw new ReflectionException(e); } catch (NoSuchMethodException e) { throw new ReflectionException(context.getBeanClass().getName() + "." + propertyName + ": Cannot convert from String to " + paramType.getSimpleName() + ". Trying to convert " + value); } catch (InstantiationException e) { throw new ReflectionException(e); } } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); nestedWrapper.setValueOfString(nestedProperty, value); } } /** * Sets the value of a property in the wrapped bean. If that property references another bean (a nested bean) Its * property values can also be set by specifying a property path.
* For example setValue("pro1.prop2", Boolean.TRUE) will set the value of prop2 from the nested bean * referenced by prop1. If the value of prop1 was originally null, it would also be set to reference the new bean * holding the value of prop2
* For each nested bean referenced in this manner, a nested bean wrapper is automatically created. In the previous * example, a bean wrapper would be created for the bean referenced by property prop1.
* * @param propertyName * @param value * value to be set */ public void setValue(String propertyName, Object value) { if(getBean() == null) createInstance(); int nestedIndex = propertyName.indexOf('.'); if (nestedIndex == -1) { // not a nested property context.getBeanFieldHandler(propertyName).setValue(this, value); } else { // its a nested property String thisProperty = propertyName.substring(0, nestedIndex); String nestedProperty = propertyName.substring(nestedIndex + 1); BeanWrapper nestedWrapper = getNestedWrapper(thisProperty); nestedWrapper.setValue(nestedProperty, value); } } /** * Obtains the BeanWrapper that corresponds to the bean instance of this property type. * @param thisProperty property name in this bean wrapper. It must be of bean type. * @return */ public BeanWrapper getNestedWrapper(String thisProperty) { BeanWrapper nestedWrapper = nestedWrappers.get(thisProperty); if (nestedWrapper == null) { Object value = getValue(thisProperty); BeanWrapperContext bwc = context.getNestedContext(thisProperty, value); nestedWrapper = new BeanWrapper(bwc, this, thisProperty); nestedWrappers.put(thisProperty, nestedWrapper); } return nestedWrapper; } protected BeanWrapper getParentBeanWrapper() { return parentBeanWrapper; } protected String getParentBeanProperty() { return parentBeanProperty; } private void createInstance() { Object instance = context.newBeanInstance(); if( isRoot() ) wrappedInstance = instance; else parentBeanWrapper.setValue(parentBeanProperty, instance); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy