WrapDynaBean.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */

  17. package org.apache.commons.beanutils2;

  18. import java.lang.reflect.InvocationTargetException;

  19. /**
  20.  * <p>
  21.  * Implements {@code DynaBean} to wrap a standard JavaBean instance, so that DynaBean APIs can be used to access its properties.
  22.  * </p>
  23.  *
  24.  * <p>
  25.  * The most common use cases for this class involve wrapping an existing Java bean. (This makes it different from the typical use cases for other
  26.  * {@code DynaBean}'s.) For example:
  27.  * </p>
  28.  *
  29.  * <pre>{@code
  30.  *  Object aJavaBean = ...;
  31.  *  ...
  32.  *  DynaBean db = new WrapDynaBean(aJavaBean);
  33.  *  ...
  34.  * }</pre>
  35.  *
  36.  * <p>
  37.  * <strong>IMPLEMENTATION NOTE</strong> - This implementation does not support the {@code contains()</code> and <code>remove()} methods.
  38.  * </p>
  39.  */

  40. public class WrapDynaBean implements DynaBean {

  41.     private static final long serialVersionUID = 1L;

  42.     /**
  43.      * The {@code DynaClass} "base class" that this DynaBean is associated with.
  44.      */
  45.     protected transient WrapDynaClass dynaClass;

  46.     /**
  47.      * The JavaBean instance wrapped by this WrapDynaBean.
  48.      */
  49.     protected Object instance;

  50.     /**
  51.      * Constructs a new {@code DynaBean} associated with the specified JavaBean instance.
  52.      *
  53.      * @param instance JavaBean instance to be wrapped
  54.      */
  55.     public WrapDynaBean(final Object instance) {
  56.         this(instance, null);
  57.     }

  58.     /**
  59.      * Creates a new instance of {@code WrapDynaBean}, associates it with the specified JavaBean instance, and initializes the bean's {@code DynaClass}. Using
  60.      * this constructor this {@code WrapDynaBean} instance can be assigned a class which has been configured externally. If no {@code WrapDynaClass} is
  61.      * provided, a new one is created using a standard mechanism.
  62.      *
  63.      * @param instance JavaBean instance to be wrapped
  64.      * @param cls      the optional {@code WrapDynaClass} to be used for this bean
  65.      * @since 1.9
  66.      */
  67.     public WrapDynaBean(final Object instance, final WrapDynaClass cls) {
  68.         this.instance = instance;
  69.         this.dynaClass = cls != null ? cls : (WrapDynaClass) getDynaClass();
  70.     }

  71.     /**
  72.      * Does the specified mapped property contain a value for the specified key value?
  73.      *
  74.      * @param name Name of the property to check
  75.      * @param key  Name of the key to check
  76.      * @return {@code true} if the mapped property contains a value for the specified key, otherwise {@code false}
  77.      * @throws IllegalArgumentException if there is no property of the specified name
  78.      */
  79.     @Override
  80.     public boolean contains(final String name, final String key) {
  81.         throw new UnsupportedOperationException("WrapDynaBean does not support contains()");
  82.     }

  83.     /**
  84.      * Gets the value of a simple property with the specified name.
  85.      *
  86.      * @param name Name of the property whose value is to be retrieved
  87.      * @return The property's value
  88.      * @throws IllegalArgumentException if there is no property of the specified name
  89.      * @throws NullPointerException     for null input.
  90.      */
  91.     @Override
  92.     public Object get(final String name) {
  93.         Object value = null;
  94.         try {
  95.             value = getPropertyUtils().getSimpleProperty(instance, name);
  96.         } catch (final InvocationTargetException ite) {
  97.             final Throwable cause = ite.getTargetException();
  98.             throw new IllegalArgumentException("Error reading property '" + name + "' nested exception - " + cause);
  99.         } catch (final NullPointerException t) {
  100.             throw t;
  101.         } catch (final Throwable t) {
  102.             throw new IllegalArgumentException("Error reading property '" + name + "', exception - " + t);
  103.         }
  104.         return value;
  105.     }

  106.     /**
  107.      * Gets the value of an indexed property with the specified name.
  108.      *
  109.      * @param name  Name of the property whose value is to be retrieved
  110.      * @param index Index of the value to be retrieved
  111.      * @return The indexed property's value
  112.      * @throws IllegalArgumentException  if there is no property of the specified name
  113.      * @throws IllegalArgumentException  if the specified property exists, but is not indexed
  114.      * @throws IndexOutOfBoundsException if the specified index is outside the range of the underlying property
  115.      * @throws NullPointerException      if no array or List has been initialized for this property
  116.      */
  117.     @Override
  118.     public Object get(final String name, final int index) {
  119.         Object value = null;
  120.         try {
  121.             value = getPropertyUtils().getIndexedProperty(instance, name, index);
  122.         } catch (final IndexOutOfBoundsException e) {
  123.             throw e;
  124.         } catch (final InvocationTargetException ite) {
  125.             final Throwable cause = ite.getTargetException();
  126.             throw new IllegalArgumentException("Error reading indexed property '" + name + "' nested exception - " + cause);
  127.         } catch (final Throwable t) {
  128.             throw new IllegalArgumentException("Error reading indexed property '" + name + "', exception - " + t);
  129.         }
  130.         return value;
  131.     }

  132.     /**
  133.      * Gets the value of a mapped property with the specified name, or {@code null} if there is no value for the specified key.
  134.      *
  135.      * @param name Name of the property whose value is to be retrieved
  136.      * @param key  Key of the value to be retrieved
  137.      * @return The mapped property's value
  138.      * @throws IllegalArgumentException if there is no property of the specified name
  139.      * @throws IllegalArgumentException if the specified property exists, but is not mapped
  140.      */
  141.     @Override
  142.     public Object get(final String name, final String key) {
  143.         Object value = null;
  144.         try {
  145.             value = getPropertyUtils().getMappedProperty(instance, name, key);
  146.         } catch (final InvocationTargetException ite) {
  147.             final Throwable cause = ite.getTargetException();
  148.             throw new IllegalArgumentException("Error reading mapped property '" + name + "' nested exception - " + cause);
  149.         } catch (final Throwable t) {
  150.             throw new IllegalArgumentException("Error reading mapped property '" + name + "', exception - " + t);
  151.         }
  152.         return value;
  153.     }

  154.     /**
  155.      * Gets the {@code DynaClass} instance that describes the set of properties available for this DynaBean.
  156.      *
  157.      * @return The associated DynaClass
  158.      */
  159.     @Override
  160.     public DynaClass getDynaClass() {
  161.         if (dynaClass == null) {
  162.             dynaClass = WrapDynaClass.createDynaClass(instance.getClass());
  163.         }

  164.         return this.dynaClass;
  165.     }

  166.     /**
  167.      * Gets the property descriptor for the specified property name.
  168.      *
  169.      * @param name Name of the property for which to retrieve the descriptor
  170.      * @return The descriptor for the specified property
  171.      * @throws IllegalArgumentException if this is not a valid property name for our DynaClass
  172.      */
  173.     protected DynaProperty getDynaProperty(final String name) {
  174.         final DynaProperty descriptor = getDynaClass().getDynaProperty(name);
  175.         if (descriptor == null) {
  176.             throw new IllegalArgumentException("Invalid property name '" + name + "'");
  177.         }
  178.         return descriptor;
  179.     }

  180.     /**
  181.      * Gets the bean instance wrapped by this DynaBean. For most common use cases, this object should already be known and this method safely be ignored. But
  182.      * some creators of frameworks using {@code DynaBean}'s may find this useful.
  183.      *
  184.      * @return the Java bean Object wrapped by this {@code DynaBean}
  185.      */
  186.     public Object getInstance() {
  187.         return instance;
  188.     }

  189.     /**
  190.      * Returns the {@code PropertyUtilsBean} instance to be used for accessing properties. If available, this object is obtained from the associated
  191.      * {@code WrapDynaClass}.
  192.      *
  193.      * @return the associated {@code PropertyUtilsBean}
  194.      */
  195.     private PropertyUtilsBean getPropertyUtils() {
  196.         PropertyUtilsBean propUtils = null;
  197.         if (dynaClass != null) {
  198.             propUtils = dynaClass.getPropertyUtilsBean();
  199.         }
  200.         return propUtils != null ? propUtils : PropertyUtilsBean.getInstance();
  201.     }

  202.     /**
  203.      * Remove any existing value for the specified key on the specified mapped property.
  204.      *
  205.      * @param name Name of the property for which a value is to be removed
  206.      * @param key  Key of the value to be removed
  207.      * @throws IllegalArgumentException if there is no property of the specified name
  208.      */
  209.     @Override
  210.     public void remove(final String name, final String key) {
  211.         throw new UnsupportedOperationException("WrapDynaBean does not support remove()");
  212.     }

  213.     /**
  214.      * Sets the value of an indexed property with the specified name.
  215.      *
  216.      * @param name  Name of the property whose value is to be set
  217.      * @param index Index of the property to be set
  218.      * @param value Value to which this property is to be set
  219.      * @throws ConversionException       if the specified value cannot be converted to the type required for this property
  220.      * @throws IllegalArgumentException  if there is no property of the specified name
  221.      * @throws IllegalArgumentException  if the specified property exists, but is not indexed
  222.      * @throws IndexOutOfBoundsException if the specified index is outside the range of the underlying property
  223.      */
  224.     @Override
  225.     public void set(final String name, final int index, final Object value) {
  226.         try {
  227.             getPropertyUtils().setIndexedProperty(instance, name, index, value);
  228.         } catch (final IndexOutOfBoundsException e) {
  229.             throw e;
  230.         } catch (final InvocationTargetException ite) {
  231.             final Throwable cause = ite.getTargetException();
  232.             throw new IllegalArgumentException("Error setting indexed property '" + name + "' nested exception - " + cause);
  233.         } catch (final Throwable t) {
  234.             throw new IllegalArgumentException("Error setting indexed property '" + name + "', exception - " + t);
  235.         }
  236.     }

  237.     /**
  238.      * Sets the value of a simple property with the specified name.
  239.      *
  240.      * @param name  Name of the property whose value is to be set
  241.      * @param value Value to which this property is to be set
  242.      * @throws ConversionException      if the specified value cannot be converted to the type required for this property
  243.      * @throws IllegalArgumentException if there is no property of the specified name
  244.      * @throws NullPointerException     if an attempt is made to set a primitive property to null
  245.      */
  246.     @Override
  247.     public void set(final String name, final Object value) {
  248.         try {
  249.             getPropertyUtils().setSimpleProperty(instance, name, value);
  250.         } catch (final InvocationTargetException ite) {
  251.             final Throwable cause = ite.getTargetException();
  252.             throw new IllegalArgumentException("Error setting property '" + name + "' nested exception -" + cause);
  253.         } catch (final Throwable t) {
  254.             throw new IllegalArgumentException("Error setting property '" + name + "', exception - " + t);
  255.         }
  256.     }

  257.     /**
  258.      * Sets the value of a mapped property with the specified name.
  259.      *
  260.      * @param name  Name of the property whose value is to be set
  261.      * @param key   Key of the property to be set
  262.      * @param value Value to which this property is to be set
  263.      * @throws ConversionException      if the specified value cannot be converted to the type required for this property
  264.      * @throws IllegalArgumentException if there is no property of the specified name
  265.      * @throws IllegalArgumentException if the specified property exists, but is not mapped
  266.      */
  267.     @Override
  268.     public void set(final String name, final String key, final Object value) {
  269.         try {
  270.             getPropertyUtils().setMappedProperty(instance, name, key, value);
  271.         } catch (final InvocationTargetException ite) {
  272.             final Throwable cause = ite.getTargetException();
  273.             throw new IllegalArgumentException("Error setting mapped property '" + name + "' nested exception - " + cause);
  274.         } catch (final Throwable t) {
  275.             throw new IllegalArgumentException("Error setting mapped property '" + name + "', exception - " + t);
  276.         }
  277.     }
  278. }