Package org.pentaho.reporting.engine.classic.core.util.beans

Source Code of org.pentaho.reporting.engine.classic.core.util.beans.BeanUtility$PropertySpecification

/*
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* Copyright (c) 2001 - 2009 Object Refinery Ltd, Pentaho Corporation and Contributors..  All rights reserved.
*/

package org.pentaho.reporting.engine.classic.core.util.beans;

import java.beans.BeanInfo;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
* The BeanUtility class enables access to bean properties using the reflection API.
*
* @author Thomas Morgner
*/
public final class BeanUtility
{
  private static final Log logger = LogFactory.getLog(BeanUtility.class);

  /**
   * A property specification parses a compound property name into segments and allows access to the next property.
   */
  private static class PropertySpecification
  {
    /**
     * The raw value of the property name.
     */
    private String raw;
    /**
     * The next direct property that should be accessed.
     */
    private String name;
    /**
     * The index, if the named property points to an indexed property.
     */
    private String index;

    /**
     * Creates a new PropertySpecification object for the given property string.
     *
     * @param raw the property string, posssibly with index specifications.
     */
    private PropertySpecification(final String raw)
    {
      this.raw = raw;
      this.name = getNormalizedName(raw);
      this.index = getIndex(raw);
    }

    /**
     * Returns the name of the property without any index information.
     *
     * @param property the raw name
     * @return the normalized name.
     */
    private String getNormalizedName(final String property)
    {
      final int idx = property.indexOf('[');
      if (idx < 0)
      {
        return property;
      }
      return property.substring(0, idx);
    }

    /**
     * Extracts the first index from the given raw property.
     *
     * @param property the raw name
     * @return the index as String.
     */
    private String getIndex(final String property)
    {
      final int idx = property.indexOf('[');
      if (idx < 0)
      {
        return null;
      }
      final int end = property.indexOf(']', idx + 1);
      if (end < 0)
      {
        return null;
      }
      return property.substring(idx + 1, end);
    }

    public String getRaw()
    {
      return raw;
    }

    public String getName()
    {
      return name;
    }

    public String getIndex()
    {
      return index;
    }

    public String toString()
    {
      final StringBuffer b = new StringBuffer("PropertySpecification={");
      b.append("raw=");
      b.append(raw);
      b.append('}');
      return b.toString();
    }
  }

  private BeanInfo beanInfo;
  private Object bean;
  private HashMap properties;

  public BeanUtility(final Object o)
      throws IntrospectionException
  {
    beanInfo = Introspector.getBeanInfo(o.getClass());
    bean = o;
    properties = new HashMap();

    final PropertyDescriptor[] propertyDescriptors =
        beanInfo.getPropertyDescriptors();
    for (int i = 0; i < propertyDescriptors.length; i++)
    {
      properties.put(propertyDescriptors[i].getName(), propertyDescriptors[i]);
    }
  }

  public void reconfigure(final Object o) throws IntrospectionException
  {
    if (bean.getClass().equals(o.getClass()))
    {
      bean = o;
    }
    else
    {
      beanInfo = Introspector.getBeanInfo(o.getClass());
      bean = o;
      properties.clear();

      final PropertyDescriptor[] propertyDescriptors =
          beanInfo.getPropertyDescriptors();
      for (int i = 0; i < propertyDescriptors.length; i++)
      {
        properties.put(propertyDescriptors[i].getName(), propertyDescriptors[i]);
      }
    }
  }

  public PropertyDescriptor[] getPropertyInfos()
  {
    return beanInfo.getPropertyDescriptors();
  }

  public Object getProperty(final String name)
      throws BeanException
  {
    return getPropertyForSpecification(new PropertySpecification(name));
  }

  private Object getPropertyForSpecification(final PropertySpecification name)
      throws BeanException
  {
    final PropertyDescriptor pd = (PropertyDescriptor) properties.get(name.getName());
    if (pd == null)
    {
      throw new BeanException("No such property:" + name);
    }

    if (pd instanceof IndexedPropertyDescriptor && name.getIndex() != null)
    {
      final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
      final Method readMethod = ipd.getIndexedReadMethod();
      if (readMethod == null)
      {
        throw new BeanException("Property is not readable: " + name);
      }
      try
      {
        return readMethod.invoke(bean, new Object[]{new Integer(name.getIndex())});
      }
      catch (Exception e)
      {
        throw BeanException.getInstance("InvokationError", e);
      }
    }
    else
    {
      final Method readMethod = pd.getReadMethod();
      if (readMethod == null)
      {
        throw BeanException.getInstance("Property is not readable: " + name, null);
      }
      if (name.getIndex() != null)
      {
        // handle access to array-only properties ..
        try
        {
          //System.out.println(readMethod);
          final Object value = readMethod.invoke(bean, null);
          // we have (possibly) an array.
          if (value == null)
          {
            //noinspection ThrowCaughtLocally
            throw new IndexOutOfBoundsException("No such index, property is null");
          }
          if (value.getClass().isArray() == false)
          {
            //noinspection ThrowCaughtLocally
            throw new BeanException("The property contains no array.");
          }
          final int index = Integer.parseInt(name.getIndex());
          return Array.get(value, index);
        }
        catch (BeanException be)
        {
          throw be;
        }
        catch (IndexOutOfBoundsException iob)
        {
          throw iob;
        }
        catch (Exception e)
        {
          throw new BeanException("Failed to read indexed property.");
        }
      }

      try
      {
        return readMethod.invoke(bean, null);
      }
      catch (Exception e)
      {
        throw BeanException.getInstance("InvokationError", e);
      }
    }
  }

  public String getPropertyAsString(final String name)
      throws BeanException
  {
    final PropertySpecification ps = new PropertySpecification(name);
    final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
    if (pd == null)
    {
      throw new BeanException("No such property:" + name);
    }
    final Object o = getPropertyForSpecification(ps);
    if (o == null)
    {
      return null;
    }

    final ValueConverter vc =
        ConverterRegistry.getInstance().getValueConverter(o.getClass());
    if (vc == null)
    {
      throw new BeanException("Unable to handle property of type " + o.getClass()
          .getName());
    }
    return vc.toAttributeValue(o);
  }

  public void setProperty(final String name, final Object o)
      throws BeanException
  {
    if (name == null)
    {
      throw new NullPointerException("Name must not be null");
    }
    setProperty(new PropertySpecification(name), o);
  }

  private void setProperty(final PropertySpecification name, final Object o)
      throws BeanException
  {
    final PropertyDescriptor pd = (PropertyDescriptor) properties.get(name.getName());
    if (pd == null)
    {
      throw new BeanException("No such property:" + name);
    }

    if (pd instanceof IndexedPropertyDescriptor && name.getIndex() != null)
    {
      final IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
      final Method writeMethod = ipd.getIndexedWriteMethod();
      if (writeMethod != null)
      {
        try
        {
          writeMethod.invoke(bean, new Object[]{new Integer(name.getIndex()), o});
        }
        catch (Exception e)
        {
          throw BeanException.getInstance("InvokationError", e);
        }
        // we've done the job ...
        return;
      }
    }

    final Method writeMethod = pd.getWriteMethod();
    if (writeMethod == null)
    {
      throw BeanException.getInstance("Property is not writeable: " + name, null);
    }

    if (name.getIndex() != null)
    {
      // this is a indexed access, but no indexWrite method was found ...
      updateArrayProperty(pd, name, o);
    }
    else
    {
      try
      {
        writeMethod.invoke(bean, new Object[]{o});
      }
      catch (Exception e)
      {
        throw BeanException.getInstance("InvokationError", e);
      }
    }
  }

  private void updateArrayProperty(final PropertyDescriptor pd,
                                   final PropertySpecification name,
                                   final Object o)
      throws BeanException
  {
    final Method readMethod = pd.getReadMethod();
    if (readMethod == null)
    {
      throw new BeanException("Property is not readable, cannot perform array update: " + name);
    }
    try
    {
      //System.out.println(readMethod);
      final Object value = readMethod.invoke(bean, null);
      // we have (possibly) an array.
      final int index = Integer.parseInt(name.getIndex());
      final Object array = validateArray(BeanUtility.getPropertyType(pd), value, index);
      Array.set(array, index, o);

      final Method writeMethod = pd.getWriteMethod();
      writeMethod.invoke(bean, new Object[]{array});
    }
    catch (BeanException e)
    {
      throw e;
    }
    catch (Exception e)
    {
      BeanUtility.logger.warn("Failed to read property, cannot perform array update: " + name, e);
      throw new BeanException("Failed to read property, cannot perform array update: " + name);
    }
  }

  /**
   * @noinspection SuspiciousSystemArraycopy
   */
  private Object validateArray(final Class propertyType,
                               final Object o, final int minArrayIndexValue)
      throws BeanException
  {

    if (propertyType.isArray() == false)
    {
      throw new BeanException("The property's value is no array.");
    }

    if (o == null)
    {
      return Array.newInstance(propertyType.getComponentType(), minArrayIndexValue + 1);
    }

    if (o.getClass().isArray() == false)
    {
      throw new BeanException("The property's value is no array.");
    }

    final int length = Array.getLength(o);
    if (length > minArrayIndexValue)
    {
      return o;
    }
    // we have to copy the array ..
    final Object retval = Array.newInstance(o.getClass().getComponentType(), minArrayIndexValue + 1);
    System.arraycopy(o, 0, retval, 0, length);
    return o;
  }

  public void setPropertyAsString(final String name, final String txt)
      throws BeanException
  {
    if (name == null)
    {
      throw new NullPointerException("Name must not be null");
    }
    if (txt == null)
    {
      throw new NullPointerException("Text must not be null");
    }
    final PropertySpecification ps = new PropertySpecification(name);
    final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
    if (pd == null)
    {
      throw new BeanException("No such property:" + name);
    }

    setPropertyAsString(name, BeanUtility.getPropertyType(pd), txt);
  }

  public Class getPropertyType(final String name) throws BeanException
  {
    if (name == null)
    {
      throw new NullPointerException("Name must not be null");
    }
    final PropertySpecification ps = new PropertySpecification(name);
    final PropertyDescriptor pd = (PropertyDescriptor) properties.get(ps.getName());
    if (pd == null)
    {
      throw new BeanException("No such property:" + name);
    }
    return BeanUtility.getPropertyType(pd);
  }

  public static Class getPropertyType(final PropertyDescriptor pd)
      throws BeanException
  {
    final Class typeFromDescriptor = pd.getPropertyType();
    if (typeFromDescriptor != null)
    {
      return typeFromDescriptor;
    }
    if (pd instanceof IndexedPropertyDescriptor)
    {
      final IndexedPropertyDescriptor idx = (IndexedPropertyDescriptor) pd;
      return idx.getIndexedPropertyType();
    }
    throw new BeanException("Unable to determine the property type.");
  }

  public void setPropertyAsString(final String name, final Class type, final String txt)
      throws BeanException
  {
    if (name == null)
    {
      throw new NullPointerException("Name must not be null");
    }
    if (type == null)
    {
      throw new NullPointerException("Type must not be null");
    }
    if (txt == null)
    {
      throw new NullPointerException("Text must not be null");
    }
    final PropertySpecification ps = new PropertySpecification(name);
    final ValueConverter vc;
    if (ps.getIndex() != null && type.isArray())
    {
      vc = ConverterRegistry.getInstance().getValueConverter(type.getComponentType());
    }
    else
    {
      vc = ConverterRegistry.getInstance().getValueConverter(type);
    }
    if (vc == null)
    {
      throw new BeanException
          ("Unable to handle '" + type + "' for property '" + name + '\'');
    }
    final Object o = vc.toPropertyValue(txt);
    setProperty(ps, o);
  }

  public String[] getProperties()
      throws BeanException
  {
    final ArrayList propertyNames = new ArrayList();
    final PropertyDescriptor[] pd = getPropertyInfos();
    for (int i = 0; i < pd.length; i++)
    {
      final PropertyDescriptor property = pd[i];
      if (property.isHidden())
      {
        continue;
      }
      if (property.getReadMethod() == null ||
          property.getWriteMethod() == null)
      {
        // it will make no sense to write a property now, that
        // we can't read in later...
        continue;
      }
      if (BeanUtility.getPropertyType(property).isArray())
      {
        final int max = findMaximumIndex(property);
        for (int idx = 0; idx < max; idx++)
        {
          propertyNames.add(property.getName() + '[' + idx + ']');
        }
      }
      else
      {
        propertyNames.add(property.getName());
      }
    }
    return (String[]) propertyNames.toArray(new String[propertyNames.size()]);
  }

  private int findMaximumIndex(final PropertyDescriptor id)
  {
    try
    {
      final Object o = getPropertyForSpecification
          (new PropertySpecification(id.getName()));
      return Array.getLength(o);
    }
    catch (Exception e)
    {
      // ignore, we run 'til we encounter an index out of bounds Ex.
    }
    return 0;
  }

  public static boolean isSameType(final Class declared, final Class object)
  {
    if (declared.equals(object))
    {
      return true;
    }

    if (Float.TYPE.equals(declared) && Float.class.equals(object))
    {
      return true;
    }
    if (Double.TYPE.equals(declared) && Double.class.equals(object))
    {
      return true;
    }
    if (Byte.TYPE.equals(declared) && Byte.class.equals(object))
    {
      return true;
    }
    if (Character.TYPE.equals(declared) && Character.class.equals(object))
    {
      return true;
    }
    if (Short.TYPE.equals(declared) && Short.class.equals(object))
    {
      return true;
    }
    if (Integer.TYPE.equals(declared) && Integer.class.equals(object))
    {
      return true;
    }
    if (Long.TYPE.equals(declared) && Long.class.equals(object))
    {
      return true;
    }
    return false;
  }
}
TOP

Related Classes of org.pentaho.reporting.engine.classic.core.util.beans.BeanUtility$PropertySpecification

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.