This is an automated email from the ASF dual-hosted git repository.

ggregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git


The following commit(s) were added to refs/heads/master by this push:
     new 9c41fcd  [BEANUTILS-527] Convert from Collections4 to 
java.util.function #8.
9c41fcd is described below

commit 9c41fcdb88209fd754c45e9aef4eff5e671c5ab0
Author: Gary Gregory <[email protected]>
AuthorDate: Sun Oct 20 18:36:27 2019 -0400

    [BEANUTILS-527] Convert from Collections4 to java.util.function #8.
    
    Use java.util.functions.Function instead of transformer.
---
 .../org/apache/commons/beanutils2/BeanMap.java     | 1574 +++++++++-----------
 .../apache/commons/beanutils2/BeanMapTestCase.java |  974 ++++++------
 2 files changed, 1232 insertions(+), 1316 deletions(-)

diff --git a/src/main/java/org/apache/commons/beanutils2/BeanMap.java 
b/src/main/java/org/apache/commons/beanutils2/BeanMap.java
index c692739..1e06488 100644
--- a/src/main/java/org/apache/commons/beanutils2/BeanMap.java
+++ b/src/main/java/org/apache/commons/beanutils2/BeanMap.java
@@ -1,829 +1,745 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-package org.apache.commons.beanutils2;
-
-import java.beans.BeanInfo;
-import java.beans.IntrospectionException;
-import java.beans.Introspector;
-import java.beans.PropertyDescriptor;
-import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.commons.collections4.Transformer;
-import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
-
-/**
- * An implementation of Map for JavaBeans which uses introspection to
- * get and put properties in the bean.
- * <p>
- * If an exception occurs during attempts to get or set a property then the
- * property is considered non existent in the Map
- *
- */
-public class BeanMap extends AbstractMap<Object, Object> implements Cloneable {
-
-    private transient Object bean;
-
-    private transient HashMap<String, Method> readMethods = new HashMap<>();
-    private transient HashMap<String, Method> writeMethods = new HashMap<>();
-    private transient HashMap<String, Class<? extends Object>> types = new 
HashMap<>();
-
-    /**
-     * An empty array.  Used to invoke accessors via reflection.
-     */
-    public static final Object[] NULL_ARGUMENTS = {};
-
-    /**
-     * Maps primitive Class types to transformers.  The transformer
-     * transform strings into the appropriate primitive wrapper.
-     *
-     * N.B. private & unmodifiable replacement for the (public & static) 
defaultTransformers instance.
-     */
-    private static final Map<Class<? extends Object>, Transformer> 
typeTransformers =
-            Collections.unmodifiableMap(createTypeTransformers());
-
-    private static Map<Class<? extends Object>, Transformer> 
createTypeTransformers() {
-        final Map<Class<? extends Object>, Transformer> defTransformers =
-                new HashMap<>();
-        defTransformers.put(
-            Boolean.TYPE,
-            input -> Boolean.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Character.TYPE,
-            input -> Character.valueOf( input.toString().charAt( 0 ) )
-        );
-        defTransformers.put(
-            Byte.TYPE,
-            input -> Byte.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Short.TYPE,
-            input -> Short.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Integer.TYPE,
-            input -> Integer.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Long.TYPE,
-            input -> Long.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Float.TYPE,
-            input -> Float.valueOf( input.toString() )
-        );
-        defTransformers.put(
-            Double.TYPE,
-            input -> Double.valueOf( input.toString() )
-        );
-        return defTransformers;
-    }
-
-
-    // Constructors
-    //-------------------------------------------------------------------------
-
-    /**
-     * Constructs a new empty <code>BeanMap</code>.
-     */
-    public BeanMap() {
-    }
-
-    /**
-     * Constructs a new <code>BeanMap</code> that operates on the
-     * specified bean.  If the given bean is <code>null</code>, then
-     * this map will be empty.
-     *
-     * @param bean  the bean for this map to operate on
-     */
-    public BeanMap(final Object bean) {
-        this.bean = bean;
-        initialise();
-    }
-
-    // Map interface
-    //-------------------------------------------------------------------------
-
-    /**
-     * Renders a string representation of this object.
-     * @return a <code>String</code> representation of this object
-     */
-    @Override
-    public String toString() {
-        return "BeanMap<" + String.valueOf(bean) + ">";
-    }
-
-    /**
-     * Clone this bean map using the following process:
-     *
-     * <ul>
-     * <li>If there is no underlying bean, return a cloned BeanMap without a
-     * bean.
-     *
-     * <li>Since there is an underlying bean, try to instantiate a new bean of
-     * the same type using Class.newInstance().
-     *
-     * <li>If the instantiation fails, throw a CloneNotSupportedException
-     *
-     * <li>Clone the bean map and set the newly instantiated bean as the
-     * underlying bean for the bean map.
-     *
-     * <li>Copy each property that is both readable and writable from the
-     * existing object to a cloned bean map.
-     *
-     * <li>If anything fails along the way, throw a
-     * CloneNotSupportedException.
-     *
-     * </ul>
-     *
-     * @return a cloned instance of this bean map
-     * @throws CloneNotSupportedException if the underlying bean
-     * cannot be cloned
-     */
-    @Override
-    public Object clone() throws CloneNotSupportedException {
-        final BeanMap newMap = (BeanMap)super.clone();
-
-        if(bean == null) {
-            // no bean, just an empty bean map at the moment.  return a newly
-            // cloned and empty bean map.
-            return newMap;
-        }
-
-        Object newBean = null;
-        final Class<? extends Object> beanClass = bean.getClass(); // Cannot 
throw Exception
-        try {
-            newBean = beanClass.newInstance();
-        } catch (final Exception e) {
-            // unable to instantiate
-            final CloneNotSupportedException cnse = new 
CloneNotSupportedException
-                ("Unable to instantiate the underlying bean \"" +
-                 beanClass.getName() + "\": " + e);
-            BeanUtils.initCause(cnse, e);
-            throw cnse;
-        }
-
-        try {
-            newMap.setBean(newBean);
-        } catch (final Exception exception) {
-            final CloneNotSupportedException cnse = new 
CloneNotSupportedException
-                ("Unable to set bean in the cloned bean map: " +
-                 exception);
-            BeanUtils.initCause(cnse, exception);
-            throw cnse;
-        }
-
-        try {
-            // copy only properties that are readable and writable.  If its
-            // not readable, we can't get the value from the old map.  If
-            // its not writable, we can't write a value into the new map.
-            for (final Object key : readMethods.keySet()) {
-                if(getWriteMethod(key) != null) {
-                    newMap.put(key, get(key));
-                }
-            }
-        } catch (final Exception exception) {
-            final CloneNotSupportedException cnse = new 
CloneNotSupportedException
-                ("Unable to copy bean values to cloned bean map: " +
-                 exception);
-            BeanUtils.initCause(cnse, exception);
-            throw cnse;
-        }
-
-        return newMap;
-    }
-
-    /**
-     * Puts all of the writable properties from the given BeanMap into this
-     * BeanMap. Read-only and Write-only properties will be ignored.
-     *
-     * @param map  the BeanMap whose properties to put
-     */
-    public void putAllWriteable(final BeanMap map) {
-        for (final Object key : map.readMethods.keySet()) {
-            if (getWriteMethod(key) != null) {
-                this.put(key, map.get(key));
-            }
-        }
-    }
-
-
-    /**
-     * This method reinitializes the bean map to have default values for the
-     * bean's properties.  This is accomplished by constructing a new instance
-     * of the bean which the map uses as its underlying data source.  This
-     * behavior for <code>clear()</code> differs from the Map contract in that
-     * the mappings are not actually removed from the map (the mappings for a
-     * BeanMap are fixed).
-     */
-    @Override
-    public void clear() {
-        if(bean == null) {
-            return;
-        }
-
-        Class<? extends Object> beanClass = null;
-        try {
-            beanClass = bean.getClass();
-            bean = beanClass.newInstance();
-        }
-        catch (final Exception e) {
-            final UnsupportedOperationException uoe =
-                new UnsupportedOperationException("Could not create new 
instance of class: " + beanClass);
-            BeanUtils.initCause(uoe, e);
-            throw uoe;
-        }
-    }
-
-    /**
-     * Returns true if the bean defines a property with the given name.
-     * <p>
-     * The given name must be a <code>String</code>; if not, this method
-     * returns false. This method will also return false if the bean
-     * does not define a property with that name.
-     * <p>
-     * Write-only properties will not be matched as the test operates against
-     * property read methods.
-     *
-     * @param name  the name of the property to check
-     * @return false if the given name is null or is not a <code>String</code>;
-     *   false if the bean does not define a property with that name; or
-     *   true if the bean does define a property with that name
-     */
-    @Override
-    public boolean containsKey(final Object name) {
-        final Method method = getReadMethod(name);
-        return method != null;
-    }
-
-    /**
-     * Returns true if the bean defines a property whose current value is
-     * the given object.
-     *
-     * @param value  the value to check
-     * @return false  true if the bean has at least one property whose
-     *   current value is that object, false otherwise
-     */
-    @Override
-    public boolean containsValue(final Object value) {
-        // use default implementation
-        return super.containsValue(value);
-    }
-
-    /**
-     * Returns the value of the bean's property with the given name.
-     * <p>
-     * The given name must be a {@link String} and must not be
-     * null; otherwise, this method returns <code>null</code>.
-     * If the bean defines a property with the given name, the value of
-     * that property is returned.  Otherwise, <code>null</code> is
-     * returned.
-     * <p>
-     * Write-only properties will not be matched as the test operates against
-     * property read methods.
-     *
-     * @param name  the name of the property whose value to return
-     * @return  the value of the property with that name
-     */
-    @Override
-    public Object get(final Object name) {
-        if ( bean != null ) {
-            final Method method = getReadMethod( name );
-            if ( method != null ) {
-                try {
-                    return method.invoke( bean, NULL_ARGUMENTS );
-                }
-                catch (  final IllegalAccessException e ) {
-                    logWarn( e );
-                }
-                catch ( final IllegalArgumentException e ) {
-                    logWarn(  e );
-                }
-                catch ( final InvocationTargetException e ) {
-                    logWarn(  e );
-                }
-                catch ( final NullPointerException e ) {
-                    logWarn(  e );
-                }
-            }
-        }
-        return null;
-    }
-
-    /**
-     * Sets the bean property with the given name to the given value.
-     *
-     * @param name  the name of the property to set
-     * @param value  the value to set that property to
-     * @return  the previous value of that property
-     * @throws IllegalArgumentException  if the given name is null;
-     *   if the given name is not a {@link String}; if the bean doesn't
-     *   define a property with that name; or if the bean property with
-     *   that name is read-only
-     * @throws ClassCastException if an error occurs creating the method args
-     */
-    @Override
-    public Object put(final Object name, final Object value) throws 
IllegalArgumentException, ClassCastException {
-        if ( bean != null ) {
-            final Object oldValue = get( name );
-            final Method method = getWriteMethod( name );
-            if ( method == null ) {
-                throw new IllegalArgumentException( "The bean of type: "+
-                        bean.getClass().getName() + " has no property called: 
" + name );
-            }
-            try {
-                final Object[] arguments = createWriteMethodArguments( method, 
value );
-                method.invoke( bean, arguments );
-
-                final Object newValue = get( name );
-                firePropertyChange( name, oldValue, newValue );
-            }
-            catch ( final InvocationTargetException e ) {
-                final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
-                if (BeanUtils.initCause(iae, e) == false) {
-                    logInfo(e);
-                }
-                throw iae;
-            }
-            catch ( final IllegalAccessException e ) {
-                final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
-                if (BeanUtils.initCause(iae, e) == false) {
-                    logInfo(e);
-                }
-                throw iae;
-            }
-            return oldValue;
-        }
-        return null;
-    }
-
-    /**
-     * Returns the number of properties defined by the bean.
-     *
-     * @return  the number of properties defined by the bean
-     */
-    @Override
-    public int size() {
-        return readMethods.size();
-    }
-
-
-    /**
-     * Get the keys for this BeanMap.
-     * <p>
-     * Write-only properties are <b>not</b> included in the returned set of
-     * property names, although it is possible to set their value and to get
-     * their type.
-     *
-     * @return BeanMap keys.  The Set returned by this method is not
-     *        modifiable.
-     */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    // The set actually contains strings; however, because it cannot be
-    // modified there is no danger in selling it as Set<Object>
-    @Override
-    public Set<Object> keySet() {
-        return Collections.unmodifiableSet((Set) readMethods.keySet());
-    }
-
-    /**
-     * Gets a Set of MapEntry objects that are the mappings for this BeanMap.
-     * <p>
-     * Each MapEntry can be set but not removed.
-     *
-     * @return the unmodifiable set of mappings
-     */
-    @Override
-    public Set<Map.Entry<Object, Object>> entrySet() {
-        return Collections.unmodifiableSet(new AbstractSet<Map.Entry<Object, 
Object>>() {
-            @Override
-            public Iterator<Map.Entry<Object, Object>> iterator() {
-                return entryIterator();
-            }
-            @Override
-            public int size() {
-              return BeanMap.this.readMethods.size();
-            }
-        });
-    }
-
-    /**
-     * Returns the values for the BeanMap.
-     *
-     * @return values for the BeanMap.  The returned collection is not
-     *        modifiable.
-     */
-    @Override
-    public Collection<Object> values() {
-        final ArrayList<Object> answer = new ArrayList<>( readMethods.size() );
-        for ( final Iterator<Object> iter = valueIterator(); iter.hasNext(); ) 
{
-            answer.add( iter.next() );
-        }
-        return Collections.unmodifiableList(answer);
-    }
-
-
-    // Helper methods
-    //-------------------------------------------------------------------------
-
-    /**
-     * Returns the type of the property with the given name.
-     *
-     * @param name  the name of the property
-     * @return  the type of the property, or <code>null</code> if no such
-     *  property exists
-     */
-    public Class<?> getType(final String name) {
-        return types.get( name );
-    }
-
-    /**
-     * Convenience method for getting an iterator over the keys.
-     * <p>
-     * Write-only properties will not be returned in the iterator.
-     *
-     * @return an iterator over the keys
-     */
-    public Iterator<String> keyIterator() {
-        return readMethods.keySet().iterator();
-    }
-
-    /**
-     * Convenience method for getting an iterator over the values.
-     *
-     * @return an iterator over the values
-     */
-    public Iterator<Object> valueIterator() {
-        final Iterator<?> iter = keyIterator();
-        return new Iterator<Object>() {
-            @Override
-            public boolean hasNext() {
-                return iter.hasNext();
-            }
-            @Override
-            public Object next() {
-                final Object key = iter.next();
-                return get(key);
-            }
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException( "remove() not 
supported for BeanMap" );
-            }
-        };
-    }
-
-    /**
-     * Convenience method for getting an iterator over the entries.
-     *
-     * @return an iterator over the entries
-     */
-    public Iterator<Map.Entry<Object, Object>> entryIterator() {
-        final Iterator<String> iter = keyIterator();
-        return new Iterator<Map.Entry<Object, Object>>() {
-            @Override
-            public boolean hasNext() {
-                return iter.hasNext();
-            }
-            @Override
-            public Map.Entry<Object, Object> next() {
-                final Object key = iter.next();
-                final Object value = get(key);
-                @SuppressWarnings("unchecked")
-                final
-                // This should not cause any problems; the key is actually a
-                // string, but it does no harm to expose it as Object
-                Map.Entry<Object, Object> tmpEntry = new Entry( BeanMap.this, 
key, value );
-                return tmpEntry;
-            }
-            @Override
-            public void remove() {
-                throw new UnsupportedOperationException( "remove() not 
supported for BeanMap" );
-            }
-        };
-    }
-
-
-    // Properties
-    //-------------------------------------------------------------------------
-
-    /**
-     * Returns the bean currently being operated on.  The return value may
-     * be null if this map is empty.
-     *
-     * @return the bean being operated on by this map
-     */
-    public Object getBean() {
-        return bean;
-    }
-
-    /**
-     * Sets the bean to be operated on by this map.  The given value may
-     * be null, in which case this map will be empty.
-     *
-     * @param newBean  the new bean to operate on
-     */
-    public void setBean( final Object newBean ) {
-        bean = newBean;
-        reinitialise();
-    }
-
-    /**
-     * Returns the accessor for the property with the given name.
-     *
-     * @param name  the name of the property
-     * @return the accessor method for the property, or null
-     */
-    public Method getReadMethod(final String name) {
-        return readMethods.get(name);
-    }
-
-    /**
-     * Returns the mutator for the property with the given name.
-     *
-     * @param name  the name of the property
-     * @return the mutator method for the property, or null
-     */
-    public Method getWriteMethod(final String name) {
-        return writeMethods.get(name);
-    }
-
-
-    // Implementation methods
-    //-------------------------------------------------------------------------
-
-    /**
-     * Returns the accessor for the property with the given name.
-     *
-     * @param name  the name of the property
-     * @return null if the name is null; null if the name is not a
-     * {@link String}; null if no such property exists; or the accessor
-     *  method for that property
-     */
-    protected Method getReadMethod( final Object name ) {
-        return readMethods.get( name );
-    }
-
-    /**
-     * Returns the mutator for the property with the given name.
-     *
-     * @param name  the name of the
-     * @return null if the name is null; null if the name is not a
-     * {@link String}; null if no such property exists; null if the
-     * property is read-only; or the mutator method for that property
-     */
-    protected Method getWriteMethod( final Object name ) {
-        return writeMethods.get( name );
-    }
-
-    /**
-     * Reinitializes this bean.  Called during {@link #setBean(Object)}.
-     * Does introspection to find properties.
-     */
-    protected void reinitialise() {
-        readMethods.clear();
-        writeMethods.clear();
-        types.clear();
-        initialise();
-    }
-
-    private void initialise() {
-        if(getBean() == null) {
-            return;
-        }
-
-        final Class<? extends Object>  beanClass = getBean().getClass();
-        try {
-            //BeanInfo beanInfo = Introspector.getBeanInfo( bean, null );
-            final BeanInfo beanInfo = Introspector.getBeanInfo( beanClass );
-            final PropertyDescriptor[] propertyDescriptors = 
beanInfo.getPropertyDescriptors();
-            if ( propertyDescriptors != null ) {
-                for (final PropertyDescriptor propertyDescriptor : 
propertyDescriptors) {
-                    if ( propertyDescriptor != null ) {
-                        final String name = propertyDescriptor.getName();
-                        final Method readMethod = 
propertyDescriptor.getReadMethod();
-                        final Method writeMethod = 
propertyDescriptor.getWriteMethod();
-                        final Class<? extends Object> aType = 
propertyDescriptor.getPropertyType();
-
-                        if ( readMethod != null ) {
-                            readMethods.put( name, readMethod );
-                        }
-                        if ( writeMethod != null ) {
-                            writeMethods.put( name, writeMethod );
-                        }
-                        types.put( name, aType );
-                    }
-                }
-            }
-        }
-        catch ( final IntrospectionException e ) {
-            logWarn(  e );
-        }
-    }
-
-    /**
-     * Called during a successful {@link #put(Object,Object)} operation.
-     * Default implementation does nothing.  Override to be notified of
-     * property changes in the bean caused by this map.
-     *
-     * @param key  the name of the property that changed
-     * @param oldValue  the old value for that property
-     * @param newValue  the new value for that property
-     */
-    protected void firePropertyChange( final Object key, final Object 
oldValue, final Object newValue ) {
-    }
-
-    // Implementation classes
-    //-------------------------------------------------------------------------
-
-    /**
-     * Map entry used by {@link BeanMap}.
-     */
-    protected static class Entry extends AbstractMapEntry<Object, Object> {
-        private final BeanMap owner;
-
-        /**
-         * Constructs a new <code>Entry</code>.
-         *
-         * @param owner  the BeanMap this entry belongs to
-         * @param key  the key for this entry
-         * @param value  the value for this entry
-         */
-        protected Entry( final BeanMap owner, final Object key, final Object 
value ) {
-            super( key, value );
-            this.owner = owner;
-        }
-
-        /**
-         * Sets the value.
-         *
-         * @param value  the new value for the entry
-         * @return the old value for the entry
-         */
-        @Override
-        public Object setValue(final Object value) {
-            final Object key = getKey();
-            final Object oldValue = owner.get( key );
-
-            owner.put( key, value );
-            final Object newValue = owner.get( key );
-            super.setValue( newValue );
-            return oldValue;
-        }
-    }
-
-    /**
-     * Creates an array of parameters to pass to the given mutator method.
-     * If the given object is not the right type to pass to the method
-     * directly, it will be converted using {@link #convertType(Class,Object)}.
-     *
-     * @param method  the mutator method
-     * @param value  the value to pass to the mutator method
-     * @return an array containing one object that is either the given value
-     *   or a transformed value
-     * @throws IllegalAccessException if {@link #convertType(Class,Object)}
-     *   raises it
-     * @throws IllegalArgumentException if any other exception is raised
-     *   by {@link #convertType(Class,Object)}
-     * @throws ClassCastException if an error occurs creating the method args
-     */
-    protected Object[] createWriteMethodArguments( final Method method, Object 
value )
-        throws IllegalAccessException, ClassCastException {
-        try {
-            if ( value != null ) {
-                final Class<? extends Object>[] parmTypes = 
method.getParameterTypes();
-                if ( parmTypes != null && parmTypes.length > 0 ) {
-                    final Class<? extends Object> paramType = parmTypes[0];
-                    if ( ! paramType.isAssignableFrom( value.getClass() ) ) {
-                        value = convertType( paramType, value );
-                    }
-                }
-            }
-            final Object[] answer = { value };
-            return answer;
-        }
-        catch ( final InvocationTargetException e ) {
-            final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
-            if (BeanUtils.initCause(iae, e) == false) {
-                logInfo(e);
-            }
-            throw iae;
-        }
-        catch ( final InstantiationException e ) {
-            final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
-            if (BeanUtils.initCause(iae, e) == false) {
-                logInfo(e);
-            }
-            BeanUtils.initCause(iae, e);
-            throw iae;
-        }
-    }
-
-    /**
-     * Converts the given value to the given type.  First, reflection is
-     * is used to find a public constructor declared by the given class
-     * that takes one argument, which must be the precise type of the
-     * given value.  If such a constructor is found, a new object is
-     * created by passing the given value to that constructor, and the
-     * newly constructed object is returned.<P>
-     *
-     * If no such constructor exists, and the given type is a primitive
-     * type, then the given value is converted to a string using its
-     * {@link Object#toString() toString()} method, and that string is
-     * parsed into the correct primitive type using, for instance,
-     * {@link Integer#valueOf(String)} to convert the string into an
-     * <code>int</code>.<P>
-     *
-     * If no special constructor exists and the given type is not a
-     * primitive type, this method returns the original value.
-     *
-     * @param newType  the type to convert the value to
-     * @param value  the value to convert
-     * @return the converted value
-     * @throws NumberFormatException if newType is a primitive type, and
-     *  the string representation of the given value cannot be converted
-     *  to that type
-     * @throws InstantiationException  if the constructor found with
-     *  reflection raises it
-     * @throws InvocationTargetException  if the constructor found with
-     *  reflection raises it
-     * @throws IllegalAccessException  never
-     * @throws IllegalArgumentException  never
-     */
-    protected Object convertType( final Class<?> newType, final Object value )
-        throws InstantiationException, IllegalAccessException, 
IllegalArgumentException, InvocationTargetException {
-
-        // try call constructor
-        try {
-            final Constructor<?> constructor = newType.getConstructor( 
value.getClass() );
-            final Object[] arguments = { value };
-            return constructor.newInstance( arguments );
-        }
-        catch ( final NoSuchMethodException e ) {
-            // try using the transformers
-            final Transformer transformer = getTypeTransformer( newType );
-            if ( transformer != null ) {
-                return transformer.transform( value );
-            }
-            return value;
-        }
-    }
-
-    /**
-     * Returns a transformer for the given primitive type.
-     *
-     * @param aType  the primitive type whose transformer to return
-     * @return a transformer that will convert strings into that type,
-     *  or null if the given type is not a primitive type
-     */
-    protected Transformer getTypeTransformer( final Class<?> aType ) {
-        return typeTransformers.get( aType );
-    }
-
-    /**
-     * Logs the given exception to <code>System.out</code>.  Used to display
-     * warnings while accessing/mutating the bean.
-     *
-     * @param ex  the exception to log
-     */
-    protected void logInfo(final Exception ex) {
-        // Deliberately do not use LOG4J or Commons Logging to avoid 
dependencies
-        System.out.println( "INFO: Exception: " + ex );
-    }
-
-    /**
-     * Logs the given exception to <code>System.err</code>.  Used to display
-     * errors while accessing/mutating the bean.
-     *
-     * @param ex  the exception to log
-     */
-    protected void logWarn(final Exception ex) {
-        // Deliberately do not use LOG4J or Commons Logging to avoid 
dependencies
-        System.out.println( "WARN: Exception: " + ex );
-        ex.printStackTrace();
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.commons.beanutils2;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.function.Function;
+
+import org.apache.commons.collections4.keyvalue.AbstractMapEntry;
+
+/**
+ * An implementation of Map for JavaBeans which uses introspection to get and 
put properties in the bean.
+ * <p>
+ * If an exception occurs during attempts to get or set a property then the 
property is considered non existent in the
+ * Map
+ * </p>
+ */
+public class BeanMap extends AbstractMap<Object, Object> implements Cloneable {
+
+    private transient Object bean;
+
+    private transient HashMap<String, Method> readMethods = new HashMap<>();
+    private transient HashMap<String, Method> writeMethods = new HashMap<>();
+    private transient HashMap<String, Class<? extends Object>> types = new 
HashMap<>();
+
+    /**
+     * An empty array. Used to invoke accessors via reflection.
+     */
+    public static final Object[] NULL_ARGUMENTS = {};
+
+    /**
+     * Maps primitive Class types to transformers. The transformer transform 
strings into the appropriate primitive
+     * wrapper.
+     *
+     * N.B. private & unmodifiable replacement for the (public & static) 
defaultTransformers instance.
+     */
+    private static final Map<Class<? extends Object>, Function<?, ?>> 
typeTransformers = Collections
+            .unmodifiableMap(createTypeTransformers());
+
+    private static Map<Class<? extends Object>, Function<?, ?>> 
createTypeTransformers() {
+        final Map<Class<? extends Object>, Function<?, ?>> defTransformers = 
new HashMap<>();
+        defTransformers.put(Boolean.TYPE, input -> 
Boolean.valueOf(input.toString()));
+        defTransformers.put(Character.TYPE, input -> 
Character.valueOf(input.toString().charAt(0)));
+        defTransformers.put(Byte.TYPE, input -> 
Byte.valueOf(input.toString()));
+        defTransformers.put(Short.TYPE, input -> 
Short.valueOf(input.toString()));
+        defTransformers.put(Integer.TYPE, input -> 
Integer.valueOf(input.toString()));
+        defTransformers.put(Long.TYPE, input -> 
Long.valueOf(input.toString()));
+        defTransformers.put(Float.TYPE, input -> 
Float.valueOf(input.toString()));
+        defTransformers.put(Double.TYPE, input -> 
Double.valueOf(input.toString()));
+        return defTransformers;
+    }
+
+    // Constructors
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Constructs a new empty <code>BeanMap</code>.
+     */
+    public BeanMap() {
+    }
+
+    /**
+     * Constructs a new <code>BeanMap</code> that operates on the specified 
bean. If the given bean is
+     * <code>null</code>, then this map will be empty.
+     *
+     * @param bean the bean for this map to operate on
+     */
+    public BeanMap(final Object bean) {
+        this.bean = bean;
+        initialise();
+    }
+
+    // Map interface
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Renders a string representation of this object.
+     * 
+     * @return a <code>String</code> representation of this object
+     */
+    @Override
+    public String toString() {
+        return "BeanMap<" + String.valueOf(bean) + ">";
+    }
+
+    /**
+     * Clone this bean map using the following process:
+     *
+     * <ul>
+     * <li>If there is no underlying bean, return a cloned BeanMap without a 
bean.
+     *
+     * <li>Since there is an underlying bean, try to instantiate a new bean of 
the same type using Class.newInstance().
+     *
+     * <li>If the instantiation fails, throw a CloneNotSupportedException
+     *
+     * <li>Clone the bean map and set the newly instantiated bean as the 
underlying bean for the bean map.
+     *
+     * <li>Copy each property that is both readable and writable from the 
existing object to a cloned bean map.
+     *
+     * <li>If anything fails along the way, throw a CloneNotSupportedException.
+     *
+     * </ul>
+     *
+     * @return a cloned instance of this bean map
+     * @throws CloneNotSupportedException if the underlying bean cannot be 
cloned
+     */
+    @Override
+    public Object clone() throws CloneNotSupportedException {
+        final BeanMap newMap = (BeanMap) super.clone();
+
+        if (bean == null) {
+            // no bean, just an empty bean map at the moment. return a newly
+            // cloned and empty bean map.
+            return newMap;
+        }
+
+        Object newBean = null;
+        final Class<? extends Object> beanClass = bean.getClass(); // Cannot 
throw Exception
+        try {
+            newBean = beanClass.newInstance();
+        } catch (final Exception e) {
+            // unable to instantiate
+            final CloneNotSupportedException cnse = new 
CloneNotSupportedException(
+                    "Unable to instantiate the underlying bean \"" + 
beanClass.getName() + "\": " + e);
+            BeanUtils.initCause(cnse, e);
+            throw cnse;
+        }
+
+        try {
+            newMap.setBean(newBean);
+        } catch (final Exception exception) {
+            final CloneNotSupportedException cnse = new 
CloneNotSupportedException(
+                    "Unable to set bean in the cloned bean map: " + exception);
+            BeanUtils.initCause(cnse, exception);
+            throw cnse;
+        }
+
+        try {
+            // copy only properties that are readable and writable. If its
+            // not readable, we can't get the value from the old map. If
+            // its not writable, we can't write a value into the new map.
+            for (final Object key : readMethods.keySet()) {
+                if (getWriteMethod(key) != null) {
+                    newMap.put(key, get(key));
+                }
+            }
+        } catch (final Exception exception) {
+            final CloneNotSupportedException cnse = new 
CloneNotSupportedException(
+                    "Unable to copy bean values to cloned bean map: " + 
exception);
+            BeanUtils.initCause(cnse, exception);
+            throw cnse;
+        }
+
+        return newMap;
+    }
+
+    /**
+     * Puts all of the writable properties from the given BeanMap into this 
BeanMap. Read-only and Write-only properties
+     * will be ignored.
+     *
+     * @param map the BeanMap whose properties to put
+     */
+    public void putAllWriteable(final BeanMap map) {
+        for (final Object key : map.readMethods.keySet()) {
+            if (getWriteMethod(key) != null) {
+                this.put(key, map.get(key));
+            }
+        }
+    }
+
+    /**
+     * This method reinitializes the bean map to have default values for the 
bean's properties. This is accomplished by
+     * constructing a new instance of the bean which the map uses as its 
underlying data source. This behavior for
+     * <code>clear()</code> differs from the Map contract in that the mappings 
are not actually removed from the map
+     * (the mappings for a BeanMap are fixed).
+     */
+    @Override
+    public void clear() {
+        if (bean == null) {
+            return;
+        }
+
+        Class<? extends Object> beanClass = null;
+        try {
+            beanClass = bean.getClass();
+            bean = beanClass.newInstance();
+        } catch (final Exception e) {
+            final UnsupportedOperationException uoe = new 
UnsupportedOperationException(
+                    "Could not create new instance of class: " + beanClass);
+            BeanUtils.initCause(uoe, e);
+            throw uoe;
+        }
+    }
+
+    /**
+     * Returns true if the bean defines a property with the given name.
+     * <p>
+     * The given name must be a <code>String</code>; if not, this method 
returns false. This method will also return
+     * false if the bean does not define a property with that name.
+     * <p>
+     * Write-only properties will not be matched as the test operates against 
property read methods.
+     *
+     * @param name the name of the property to check
+     * @return false if the given name is null or is not a 
<code>String</code>; false if the bean does not define a
+     *         property with that name; or true if the bean does define a 
property with that name
+     */
+    @Override
+    public boolean containsKey(final Object name) {
+        final Method method = getReadMethod(name);
+        return method != null;
+    }
+
+    /**
+     * Returns true if the bean defines a property whose current value is the 
given object.
+     *
+     * @param value the value to check
+     * @return false true if the bean has at least one property whose current 
value is that object, false otherwise
+     */
+    @Override
+    public boolean containsValue(final Object value) {
+        // use default implementation
+        return super.containsValue(value);
+    }
+
+    /**
+     * Returns the value of the bean's property with the given name.
+     * <p>
+     * The given name must be a {@link String} and must not be null; 
otherwise, this method returns <code>null</code>.
+     * If the bean defines a property with the given name, the value of that 
property is returned. Otherwise,
+     * <code>null</code> is returned.
+     * <p>
+     * Write-only properties will not be matched as the test operates against 
property read methods.
+     *
+     * @param name the name of the property whose value to return
+     * @return the value of the property with that name
+     */
+    @Override
+    public Object get(final Object name) {
+        if (bean != null) {
+            final Method method = getReadMethod(name);
+            if (method != null) {
+                try {
+                    return method.invoke(bean, NULL_ARGUMENTS);
+                } catch (final IllegalAccessException e) {
+                    logWarn(e);
+                } catch (final IllegalArgumentException e) {
+                    logWarn(e);
+                } catch (final InvocationTargetException e) {
+                    logWarn(e);
+                } catch (final NullPointerException e) {
+                    logWarn(e);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Sets the bean property with the given name to the given value.
+     *
+     * @param name the name of the property to set
+     * @param value the value to set that property to
+     * @return the previous value of that property
+     * @throws IllegalArgumentException if the given name is null; if the 
given name is not a {@link String}; if the
+     *         bean doesn't define a property with that name; or if the bean 
property with that name is read-only
+     * @throws ClassCastException if an error occurs creating the method args
+     */
+    @Override
+    public Object put(final Object name, final Object value) throws 
IllegalArgumentException, ClassCastException {
+        if (bean != null) {
+            final Object oldValue = get(name);
+            final Method method = getWriteMethod(name);
+            if (method == null) {
+                throw new IllegalArgumentException(
+                        "The bean of type: " + bean.getClass().getName() + " 
has no property called: " + name);
+            }
+            try {
+                final Object[] arguments = createWriteMethodArguments(method, 
value);
+                method.invoke(bean, arguments);
+
+                final Object newValue = get(name);
+                firePropertyChange(name, oldValue, newValue);
+            } catch (final InvocationTargetException e) {
+                final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
+                if (BeanUtils.initCause(iae, e) == false) {
+                    logInfo(e);
+                }
+                throw iae;
+            } catch (final IllegalAccessException e) {
+                final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
+                if (BeanUtils.initCause(iae, e) == false) {
+                    logInfo(e);
+                }
+                throw iae;
+            }
+            return oldValue;
+        }
+        return null;
+    }
+
+    /**
+     * Returns the number of properties defined by the bean.
+     *
+     * @return the number of properties defined by the bean
+     */
+    @Override
+    public int size() {
+        return readMethods.size();
+    }
+
+    /**
+     * Get the keys for this BeanMap.
+     * <p>
+     * Write-only properties are <b>not</b> included in the returned set of 
property names, although it is possible to
+     * set their value and to get their type.
+     *
+     * @return BeanMap keys. The Set returned by this method is not modifiable.
+     */
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    // The set actually contains strings; however, because it cannot be
+    // modified there is no danger in selling it as Set<Object>
+    @Override
+    public Set<Object> keySet() {
+        return Collections.unmodifiableSet((Set) readMethods.keySet());
+    }
+
+    /**
+     * Gets a Set of MapEntry objects that are the mappings for this BeanMap.
+     * <p>
+     * Each MapEntry can be set but not removed.
+     *
+     * @return the unmodifiable set of mappings
+     */
+    @Override
+    public Set<Map.Entry<Object, Object>> entrySet() {
+        return Collections.unmodifiableSet(new AbstractSet<Map.Entry<Object, 
Object>>() {
+            @Override
+            public Iterator<Map.Entry<Object, Object>> iterator() {
+                return entryIterator();
+            }
+
+            @Override
+            public int size() {
+                return BeanMap.this.readMethods.size();
+            }
+        });
+    }
+
+    /**
+     * Returns the values for the BeanMap.
+     *
+     * @return values for the BeanMap. The returned collection is not 
modifiable.
+     */
+    @Override
+    public Collection<Object> values() {
+        final ArrayList<Object> answer = new ArrayList<>(readMethods.size());
+        for (final Iterator<Object> iter = valueIterator(); iter.hasNext();) {
+            answer.add(iter.next());
+        }
+        return Collections.unmodifiableList(answer);
+    }
+
+    // Helper methods
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Returns the type of the property with the given name.
+     *
+     * @param name the name of the property
+     * @return the type of the property, or <code>null</code> if no such 
property exists
+     */
+    public Class<?> getType(final String name) {
+        return types.get(name);
+    }
+
+    /**
+     * Convenience method for getting an iterator over the keys.
+     * <p>
+     * Write-only properties will not be returned in the iterator.
+     *
+     * @return an iterator over the keys
+     */
+    public Iterator<String> keyIterator() {
+        return readMethods.keySet().iterator();
+    }
+
+    /**
+     * Convenience method for getting an iterator over the values.
+     *
+     * @return an iterator over the values
+     */
+    public Iterator<Object> valueIterator() {
+        final Iterator<?> iter = keyIterator();
+        return new Iterator<Object>() {
+            @Override
+            public boolean hasNext() {
+                return iter.hasNext();
+            }
+
+            @Override
+            public Object next() {
+                final Object key = iter.next();
+                return get(key);
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException("remove() not 
supported for BeanMap");
+            }
+        };
+    }
+
+    /**
+     * Convenience method for getting an iterator over the entries.
+     *
+     * @return an iterator over the entries
+     */
+    public Iterator<Map.Entry<Object, Object>> entryIterator() {
+        final Iterator<String> iter = keyIterator();
+        return new Iterator<Map.Entry<Object, Object>>() {
+            @Override
+            public boolean hasNext() {
+                return iter.hasNext();
+            }
+
+            @Override
+            public Map.Entry<Object, Object> next() {
+                final Object key = iter.next();
+                final Object value = get(key);
+                // This should not cause any problems; the key is actually a
+                // string, but it does no harm to expose it as Object
+                return new Entry(BeanMap.this, key, value);
+            }
+
+            @Override
+            public void remove() {
+                throw new UnsupportedOperationException("remove() not 
supported for BeanMap");
+            }
+        };
+    }
+
+    // Properties
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Returns the bean currently being operated on. The return value may be 
null if this map is empty.
+     *
+     * @return the bean being operated on by this map
+     */
+    public Object getBean() {
+        return bean;
+    }
+
+    /**
+     * Sets the bean to be operated on by this map. The given value may be 
null, in which case this map will be empty.
+     *
+     * @param newBean the new bean to operate on
+     */
+    public void setBean(final Object newBean) {
+        bean = newBean;
+        reinitialise();
+    }
+
+    /**
+     * Returns the accessor for the property with the given name.
+     *
+     * @param name the name of the property
+     * @return the accessor method for the property, or null
+     */
+    public Method getReadMethod(final String name) {
+        return readMethods.get(name);
+    }
+
+    /**
+     * Returns the mutator for the property with the given name.
+     *
+     * @param name the name of the property
+     * @return the mutator method for the property, or null
+     */
+    public Method getWriteMethod(final String name) {
+        return writeMethods.get(name);
+    }
+
+    // Implementation methods
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Returns the accessor for the property with the given name.
+     *
+     * @param name the name of the property
+     * @return null if the name is null; null if the name is not a {@link 
String}; null if no such property exists; or
+     *         the accessor method for that property
+     */
+    protected Method getReadMethod(final Object name) {
+        return readMethods.get(name);
+    }
+
+    /**
+     * Returns the mutator for the property with the given name.
+     *
+     * @param name the name of the
+     * @return null if the name is null; null if the name is not a {@link 
String}; null if no such property exists; null
+     *         if the property is read-only; or the mutator method for that 
property
+     */
+    protected Method getWriteMethod(final Object name) {
+        return writeMethods.get(name);
+    }
+
+    /**
+     * Reinitializes this bean. Called during {@link #setBean(Object)}. Does 
introspection to find properties.
+     */
+    protected void reinitialise() {
+        readMethods.clear();
+        writeMethods.clear();
+        types.clear();
+        initialise();
+    }
+
+    private void initialise() {
+        if (getBean() == null) {
+            return;
+        }
+
+        final Class<? extends Object> beanClass = getBean().getClass();
+        try {
+            // BeanInfo beanInfo = Introspector.getBeanInfo( bean, null );
+            final BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
+            final PropertyDescriptor[] propertyDescriptors = 
beanInfo.getPropertyDescriptors();
+            if (propertyDescriptors != null) {
+                for (final PropertyDescriptor propertyDescriptor : 
propertyDescriptors) {
+                    if (propertyDescriptor != null) {
+                        final String name = propertyDescriptor.getName();
+                        final Method readMethod = 
propertyDescriptor.getReadMethod();
+                        final Method writeMethod = 
propertyDescriptor.getWriteMethod();
+                        final Class<? extends Object> aType = 
propertyDescriptor.getPropertyType();
+
+                        if (readMethod != null) {
+                            readMethods.put(name, readMethod);
+                        }
+                        if (writeMethod != null) {
+                            writeMethods.put(name, writeMethod);
+                        }
+                        types.put(name, aType);
+                    }
+                }
+            }
+        } catch (final IntrospectionException e) {
+            logWarn(e);
+        }
+    }
+
+    /**
+     * Called during a successful {@link #put(Object,Object)} operation. 
Default implementation does nothing. Override
+     * to be notified of property changes in the bean caused by this map.
+     *
+     * @param key the name of the property that changed
+     * @param oldValue the old value for that property
+     * @param newValue the new value for that property
+     */
+    protected void firePropertyChange(final Object key, final Object oldValue, 
final Object newValue) {
+        // noop
+    }
+
+    // Implementation classes
+    // 
-------------------------------------------------------------------------
+
+    /**
+     * Map entry used by {@link BeanMap}.
+     */
+    protected static class Entry extends AbstractMapEntry<Object, Object> {
+        private final BeanMap owner;
+
+        /**
+         * Constructs a new <code>Entry</code>.
+         *
+         * @param owner the BeanMap this entry belongs to
+         * @param key the key for this entry
+         * @param value the value for this entry
+         */
+        protected Entry(final BeanMap owner, final Object key, final Object 
value) {
+            super(key, value);
+            this.owner = owner;
+        }
+
+        /**
+         * Sets the value.
+         *
+         * @param value the new value for the entry
+         * @return the old value for the entry
+         */
+        @Override
+        public Object setValue(final Object value) {
+            final Object key = getKey();
+            final Object oldValue = owner.get(key);
+
+            owner.put(key, value);
+            final Object newValue = owner.get(key);
+            super.setValue(newValue);
+            return oldValue;
+        }
+    }
+
+    /**
+     * Creates an array of parameters to pass to the given mutator method. If 
the given object is not the right type to
+     * pass to the method directly, it will be converted using {@link 
#convertType(Class,Object)}.
+     *
+     * @param method the mutator method
+     * @param value the value to pass to the mutator method
+     * @return an array containing one object that is either the given value 
or a transformed value
+     * @throws IllegalAccessException if {@link #convertType(Class,Object)} 
raises it
+     * @throws IllegalArgumentException if any other exception is raised by 
{@link #convertType(Class,Object)}
+     * @throws ClassCastException if an error occurs creating the method args
+     */
+    protected Object[] createWriteMethodArguments(final Method method, Object 
value)
+            throws IllegalAccessException, ClassCastException {
+        try {
+            if (value != null) {
+                final Class<? extends Object>[] parmTypes = 
method.getParameterTypes();
+                if (parmTypes != null && parmTypes.length > 0) {
+                    final Class<? extends Object> paramType = parmTypes[0];
+                    if (!paramType.isAssignableFrom(value.getClass())) {
+                        value = convertType(paramType, value);
+                    }
+                }
+            }
+            final Object[] answer = { value };
+            return answer;
+        } catch (final InvocationTargetException e) {
+            final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
+            if (BeanUtils.initCause(iae, e) == false) {
+                logInfo(e);
+            }
+            throw iae;
+        } catch (final InstantiationException e) {
+            final IllegalArgumentException iae = new 
IllegalArgumentException(e.getMessage());
+            if (BeanUtils.initCause(iae, e) == false) {
+                logInfo(e);
+            }
+            BeanUtils.initCause(iae, e);
+            throw iae;
+        }
+    }
+
+    /**
+     * Converts the given value to the given type. First, reflection is is 
used to find a public constructor declared by
+     * the given class that takes one argument, which must be the precise type 
of the given value. If such a constructor
+     * is found, a new object is created by passing the given value to that 
constructor, and the newly constructed
+     * object is returned.
+     * <P>
+     *
+     * If no such constructor exists, and the given type is a primitive type, 
then the given value is converted to a
+     * string using its {@link Object#toString() toString()} method, and that 
string is parsed into the correct
+     * primitive type using, for instance, {@link Integer#valueOf(String)} to 
convert the string into an
+     * <code>int</code>.
+     * <P>
+     *
+     * If no special constructor exists and the given type is not a primitive 
type, this method returns the original
+     * value.
+     *
+     * @param newType the type to convert the value to
+     * @param value the value to convert
+     * @return the converted value
+     * @throws NumberFormatException if newType is a primitive type, and the 
string representation of the given value
+     *         cannot be converted to that type
+     * @throws InstantiationException if the constructor found with reflection 
raises it
+     * @throws InvocationTargetException if the constructor found with 
reflection raises it
+     * @throws IllegalAccessException never
+     * @throws IllegalArgumentException never
+     */
+    protected Object convertType(final Class<?> newType, final Object value)
+            throws InstantiationException, IllegalAccessException, 
IllegalArgumentException, InvocationTargetException {
+
+        // try call constructor
+        try {
+            final Constructor<?> constructor = 
newType.getConstructor(value.getClass());
+            return constructor.newInstance(new Object[] { value });
+        } catch (final NoSuchMethodException e) {
+            // try using the transformers
+            final Function transformer = getTypeTransformer(newType);
+            if (transformer != null) {
+                return transformer.apply(value);
+            }
+            return value;
+        }
+    }
+
+    /**
+     * Returns a transformer for the given primitive type.
+     *
+     * @param aType the primitive type whose transformer to return
+     * @return a transformer that will convert strings into that type, or null 
if the given type is not a primitive type
+     */
+    protected Function getTypeTransformer(final Class<?> aType) {
+        return typeTransformers.get(aType);
+    }
+
+    /**
+     * Logs the given exception to <code>System.out</code>. Used to display 
warnings while accessing/mutating the bean.
+     *
+     * @param ex the exception to log
+     */
+    protected void logInfo(final Exception ex) {
+        // Deliberately do not use LOG4J or Commons Logging to avoid 
dependencies
+        System.out.println("INFO: Exception: " + ex);
+    }
+
+    /**
+     * Logs the given exception to <code>System.err</code>. Used to display 
errors while accessing/mutating the bean.
+     *
+     * @param ex the exception to log
+     */
+    protected void logWarn(final Exception ex) {
+        // Deliberately do not use LOG4J or Commons Logging to avoid 
dependencies
+        System.out.println("WARN: Exception: " + ex);
+        ex.printStackTrace();
+    }
+}
diff --git a/src/test/java/org/apache/commons/beanutils2/BeanMapTestCase.java 
b/src/test/java/org/apache/commons/beanutils2/BeanMapTestCase.java
index 695d728..e3a1d87 100644
--- a/src/test/java/org/apache/commons/beanutils2/BeanMapTestCase.java
+++ b/src/test/java/org/apache/commons/beanutils2/BeanMapTestCase.java
@@ -1,487 +1,487 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You 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.
- */
-package org.apache.commons.beanutils2;
-
-import java.io.Serializable;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.Map;
-
-import org.apache.commons.beanutils2.bugs.other.Jira87BeanFactory;
-import org.apache.commons.collections.BulkTest;
-import org.apache.commons.collections.map.AbstractTestMap;
-
-import junit.framework.Test;
-import junit.textui.TestRunner;
-
-/**
- * Test cases for BeanMap
- *
- */
-@SuppressWarnings("deprecation")
-public class BeanMapTestCase extends AbstractTestMap {
-
-    public BeanMapTestCase(final String testName) {
-        super(testName);
-    }
-
-    public static void main(final String[] args) {
-        TestRunner.run(suite());
-    }
-
-    public static Test suite() {
-        return BulkTest.makeSuite(BeanMapTestCase.class);
-    }
-
-/*
-  note to self.  The getter and setter methods were generated by copying the
-  field declarations and using the following regular expression search and
-  replace:
-
-  From:
-        private \(.*\) some\(.*\);
-  To:
-        public \1 getSome\2Value() {
-            return some\2;
-        }
-        public void setSome\2Value(\1 value) {
-            some\2 = value;
-        }
-
-  Also note:  The sample keys and mappings were generated manually.
-*/
-
-
-    public static class BeanWithProperties implements Serializable {
-        private int someInt;
-        private long someLong;
-        private double someDouble;
-        private float someFloat;
-        private short someShort;
-        private byte someByte;
-        private char someChar;
-        private Integer someInteger;
-        private String someString;
-        private Object someObject;
-
-        public int getSomeIntValue() {
-            return someInt;
-        }
-        public void setSomeIntValue(final int value) {
-            someInt = value;
-        }
-
-        public long getSomeLongValue() {
-            return someLong;
-        }
-        public void setSomeLongValue(final long value) {
-            someLong = value;
-        }
-
-        public double getSomeDoubleValue() {
-            return someDouble;
-        }
-        public void setSomeDoubleValue(final double value) {
-            someDouble = value;
-        }
-
-        public float getSomeFloatValue() {
-            return someFloat;
-        }
-        public void setSomeFloatValue(final float value) {
-            someFloat = value;
-        }
-
-        public short getSomeShortValue() {
-            return someShort;
-        }
-        public void setSomeShortValue(final short value) {
-            someShort = value;
-        }
-
-        public byte getSomeByteValue() {
-            return someByte;
-        }
-        public void setSomeByteValue(final byte value) {
-            someByte = value;
-        }
-
-        public char getSomeCharValue() {
-            return someChar;
-        }
-        public void setSomeCharValue(final char value) {
-            someChar = value;
-        }
-
-        public String getSomeStringValue() {
-            return someString;
-        }
-        public void setSomeStringValue(final String value) {
-            someString = value;
-        }
-
-        public Integer getSomeIntegerValue() {
-            return someInteger;
-        }
-        public void setSomeIntegerValue(final Integer value) {
-            someInteger = value;
-        }
-
-        public Object getSomeObjectValue() {
-            return someObject;
-        }
-        public void setSomeObjectValue(final Object value) {
-            someObject = value;
-        }
-    }
-
-    public static class BeanThrowingExceptions extends BeanWithProperties {
-        private static final long serialVersionUID = 1L;
-        public void setValueThrowingException(final String value) {
-            throw new TestException();
-        }
-        public String getValueThrowingException() {
-            throw new TestException();
-        }
-    }
-
-    /**
-     * Exception for testing exception handling.
-     */
-    public static class TestException extends RuntimeException {
-        private static final long serialVersionUID = 1L;
-    }
-
-    // note to self.  The Sample keys were generated by copying the field
-    // declarations and using the following regular expression search and 
replace:
-    //
-    // From:
-    //    private \(.*\) some\(.*\);
-    // To:
-    //    "some\2Value",
-    //
-    // Then, I manually added the "class" key, which is a property that exists 
for
-    // all beans (and all objects for that matter.
-    @Override
-    public Object[] getSampleKeys() {
-        final Object[] keys = new Object[] {
-            "someIntValue",
-            "someLongValue",
-            "someDoubleValue",
-            "someFloatValue",
-            "someShortValue",
-            "someByteValue",
-            "someCharValue",
-            "someIntegerValue",
-            "someStringValue",
-            "someObjectValue",
-            "class",
-        };
-        return keys;
-    }
-
-    /**
-     *  An object value that will be stored in the bean map as a value.  Need
-     *  to save this externally so that we can make sure the object instances
-     *  are equivalent since getSampleValues() would otherwise construct a new
-     *  and different Object each time.
-     **/
-    private final Object objectInFullMap = new Object();
-
-    // note to self: the sample values were created manually
-    @Override
-    public Object[] getSampleValues() {
-        final Object[] values = new Object[] {
-            new Integer(1234),
-            new Long(1298341928234L),
-            new Double(123423.34),
-            new Float(1213332.12f),
-            new Short((short)134),
-            new Byte((byte)10),
-            new Character('a'),
-            new Integer(1432),
-            "SomeStringValue",
-            objectInFullMap,
-            BeanWithProperties.class,
-        };
-        return values;
-    }
-
-    @Override
-    public Object[] getNewSampleValues() {
-        final Object[] values = new Object[] {
-            new Integer(223),
-            new Long(23341928234L),
-            new Double(23423.34),
-            new Float(213332.12f),
-            new Short((short)234),
-            new Byte((byte)20),
-            new Character('b'),
-            new Integer(232),
-            "SomeNewStringValue",
-            new Object(),
-            null,
-        };
-        return values;
-    }
-
-    /**
-     * Values is a dead copy in BeanMap, so refresh each time.
-     */
-    @Override
-    public void verifyValues() {
-        values = map.values();
-        super.verifyValues();
-    }
-
-    /**
-     * The mappings in a BeanMap are fixed on the properties the underlying
-     * bean has.  Adding and removing mappings is not possible, thus this
-     * method is overridden to return false.
-     */
-    @Override
-    public boolean isPutAddSupported() {
-        return false;
-    }
-
-    /**
-     * The mappings in a BeanMap are fixed on the properties the underlying
-     * bean has.  Adding and removing mappings is not possible, thus this
-     * method is overridden to return false.
-     */
-    @Override
-    public boolean isRemoveSupported() {
-        return false;
-    }
-
-    @Override
-    public Map<Object, Object> makeFullMap() {
-        // note: These values must match (i.e. .equals() must return true)
-        // those returned from getSampleValues().
-        final BeanWithProperties bean = new BeanWithProperties();
-        bean.setSomeIntValue(1234);
-        bean.setSomeLongValue(1298341928234L);
-        bean.setSomeDoubleValue(123423.34);
-        bean.setSomeFloatValue(1213332.12f);
-        bean.setSomeShortValue((short)134);
-        bean.setSomeByteValue((byte)10);
-        bean.setSomeCharValue('a');
-        bean.setSomeIntegerValue(new Integer(1432));
-        bean.setSomeStringValue("SomeStringValue");
-        bean.setSomeObjectValue(objectInFullMap);
-        return new BeanMap(bean);
-    }
-
-    @Override
-    public Map<Object, Object> makeEmptyMap() {
-        return new BeanMap();
-    }
-
-    @Override
-    public String[] ignoredTests() {
-        // Ignore the serialization tests on collection views.
-        return new String[] {
-         "TestBeanMap.bulkTestMapEntrySet.testCanonicalEmptyCollectionExists",
-         "TestBeanMap.bulkTestMapEntrySet.testCanonicalFullCollectionExists",
-         "TestBeanMap.bulkTestMapKeySet.testCanonicalEmptyCollectionExists",
-         "TestBeanMap.bulkTestMapKeySet.testCanonicalFullCollectionExists",
-         "TestBeanMap.bulkTestMapValues.testCanonicalEmptyCollectionExists",
-         "TestBeanMap.bulkTestMapValues.testCanonicalFullCollectionExists",
-         "TestBeanMap.bulkTestMapEntrySet.testSimpleSerialization",
-         "TestBeanMap.bulkTestMapKeySet.testSimpleSerialization",
-         "TestBeanMap.bulkTestMapEntrySet.testSerializeDeserializeThenCompare",
-         "TestBeanMap.bulkTestMapKeySet.testSerializeDeserializeThenCompare"
-        };
-    }
-
-    /**
-     * Need to override this method because the "clear()" method on the bean
-     * map just returns the bean properties to their default states.  It does
-     * not actually remove the mappings as per the map contract.  The default
-     * testClear() methods checks that the clear method throws an
-     * UnsupportedOperationException since this class is not add/remove
-     * modifiable.  In our case though, we do not always throw that exception.
-     */
-    @Override
-    public void testMapClear() {
-        //TODO: make sure a call to BeanMap.clear returns the bean to its
-        //default initialization values.
-    }
-
-    /**
-     * Need to override this method because the "put()" method on the bean
-     * doesn't work for this type of Map.
-     */
-    @Override
-    public void testMapPut() {
-        // see testBeanMapPutAllWriteable
-    }
-
-    public void testBeanMapClone() {
-        final BeanMap map = (BeanMap)makeFullMap();
-        try {
-            final BeanMap map2 = (BeanMap)map.clone();
-
-            // make sure containsKey is working to verify the bean was cloned
-            // ok, and the read methods were properly initialized
-            final Object[] keys = getSampleKeys();
-            for (final Object key : keys) {
-                assertTrue("Cloned BeanMap should contain the same keys",
-                           map2.containsKey(key));
-            }
-        } catch (final CloneNotSupportedException exception) {
-            fail("BeanMap.clone() should not throw a " +
-                 "CloneNotSupportedException when clone should succeed.");
-        }
-    }
-
-    public void testBeanMapPutAllWriteable() {
-        final BeanMap map1 = (BeanMap)makeFullMap();
-        final BeanMap map2 = (BeanMap)makeFullMap();
-        map2.put("someIntValue", new Integer(0));
-        map1.putAllWriteable(map2);
-        assertEquals(map1.get("someIntValue"), new Integer(0));
-    }
-
-    public void testMethodAccessor() throws Exception {
-        final BeanMap map = (BeanMap) makeFullMap();
-        final Method method = 
BeanWithProperties.class.getDeclaredMethod("getSomeIntegerValue");
-        assertEquals(method, map.getReadMethod("someIntegerValue"));
-    }
-
-    public void testMethodMutator() throws Exception {
-        final BeanMap map = (BeanMap) makeFullMap();
-        final Method method = 
BeanWithProperties.class.getDeclaredMethod("setSomeIntegerValue", new Class[] 
{Integer.class});
-        assertEquals(method, map.getWriteMethod("someIntegerValue"));
-    }
-
-    /**
-     *  Test the default transformers using the getTypeTransformer() method
-     */
-    public void testGetTypeTransformerMethod() {
-        final BeanMap beanMap = new BeanMap();
-        assertEquals("Boolean.TYPE",   Boolean.TRUE,        
beanMap.getTypeTransformer(Boolean.TYPE).transform("true"));
-        assertEquals("Character.TYPE", new Character('B'),  
beanMap.getTypeTransformer(Character.TYPE).transform("BCD"));
-        assertEquals("Byte.TYPE",      new Byte((byte)1),   
beanMap.getTypeTransformer(Byte.TYPE).transform("1"));
-        assertEquals("Short.TYPE",     new Short((short)2), 
beanMap.getTypeTransformer(Short.TYPE).transform("2"));
-        assertEquals("Integer.TYPE",   new Integer(3),      
beanMap.getTypeTransformer(Integer.TYPE).transform("3"));
-        assertEquals("Long.TYPE",      new Long(4),         
beanMap.getTypeTransformer(Long.TYPE).transform("4"));
-        assertEquals("Float.TYPE",     new Float("5"),      
beanMap.getTypeTransformer(Float.TYPE).transform("5"));
-        assertEquals("Double.TYPE",    new Double("6"),     
beanMap.getTypeTransformer(Double.TYPE).transform("6"));
-    }
-
-    /**
-     * Test that the cause of exception thrown by a clone() is initialised.
-     */
-    public void testExceptionThrowFromClone() {
-
-        if (BeanUtilsTestCase.isPre14JVM()) {
-            System.out.println("testExceptionThrowFromClone() skipped on pre 
1.4 JVM");
-            return;
-        }
-
-        // Test cloning a non-public bean (instantiation exception)
-        try {
-            final Object bean = Jira87BeanFactory.createMappedPropertyBean();
-            final BeanMap map = new BeanMap(bean);
-            map.clone();
-            fail("Non-public bean clone() - expected 
CloneNotSupportedException");
-        } catch (final CloneNotSupportedException e) {
-            Throwable cause = null;
-            try {
-                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
-            } catch (final Exception e2) {
-                fail("Non-public bean - retrieving the cause threw " + e2);
-            }
-            assertNotNull("Non-public bean cause null", cause);
-            assertEquals("Non-public bean cause", 
IllegalAccessException.class, cause.getClass());
-        }
-
-        // Test cloning a bean that throws exception
-        try {
-            final BeanMap map = new BeanMap(new BeanThrowingExceptions());
-            map.clone();
-            fail("Setter Exception clone() - expected 
CloneNotSupportedException");
-        } catch (final CloneNotSupportedException e) {
-            Throwable cause = null;
-            try {
-                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
-            } catch (final Exception e2) {
-                fail("Setter Exception - retrieving the cause threw " + e2);
-            }
-            assertNotNull("Setter Exception cause null", cause);
-            assertEquals("Setter Exception cause", 
IllegalArgumentException.class, cause.getClass());
-        }
-    }
-
-    /**
-     * Test that the cause of exception thrown by clear() is initialised.
-     */
-    public void testExceptionThrowFromClear() {
-
-        if (BeanUtilsTestCase.isPre14JVM()) {
-            System.out.println("testExceptionThrowFromClear() skipped on pre 
1.4 JVM");
-            return;
-        }
-
-        try {
-            final Object bean = Jira87BeanFactory.createMappedPropertyBean();
-            final BeanMap map = new BeanMap(bean);
-            map.clear();
-            fail("clear() - expected UnsupportedOperationException");
-        } catch (final UnsupportedOperationException e) {
-            Throwable cause = null;
-            try {
-                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
-            } catch (final Exception e2) {
-                fail("Retrieving the cause threw " + e2);
-            }
-            assertNotNull("Cause null", cause);
-            assertEquals("Cause", IllegalAccessException.class, 
cause.getClass());
-        }
-    }
-
-    /**
-     * Test that the cause of exception thrown by put() is initialized.
-     */
-    public void testExceptionThrowFromPut() {
-
-        if (BeanUtilsTestCase.isPre14JVM()) {
-            System.out.println("testExceptionThrowFromPut() skipped on pre 1.4 
JVM");
-            return;
-        }
-
-        try {
-            final Map<Object, Object> map = new BeanMap(new 
BeanThrowingExceptions());
-            map.put("valueThrowingException", "value");
-            fail("Setter exception - expected IllegalArgumentException");
-        } catch (final IllegalArgumentException e) {
-            Throwable cause1 = null;
-            Throwable cause2 = null;
-            try {
-                cause1 = (Throwable)PropertyUtils.getProperty(e, "cause");
-                cause2 = (Throwable)PropertyUtils.getProperty(e, 
"cause.cause");
-            } catch (final Exception e2) {
-                fail("Setter exception - retrieving the cause threw " + e2);
-            }
-            assertNotNull("Setter exception cause 1 null", cause1);
-            assertEquals("Setter exception cause 1", 
InvocationTargetException.class, cause1.getClass());
-            assertNotNull("Setter exception cause 2 null", cause2);
-            assertEquals("Setter exception cause 2", TestException.class, 
cause2.getClass());
-        }
-    }
-}
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ */
+package org.apache.commons.beanutils2;
+
+import java.io.Serializable;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import org.apache.commons.beanutils2.bugs.other.Jira87BeanFactory;
+import org.apache.commons.collections.BulkTest;
+import org.apache.commons.collections.map.AbstractTestMap;
+
+import junit.framework.Test;
+import junit.textui.TestRunner;
+
+/**
+ * Test cases for BeanMap
+ *
+ */
+@SuppressWarnings("deprecation")
+public class BeanMapTestCase extends AbstractTestMap {
+
+    public BeanMapTestCase(final String testName) {
+        super(testName);
+    }
+
+    public static void main(final String[] args) {
+        TestRunner.run(suite());
+    }
+
+    public static Test suite() {
+        return BulkTest.makeSuite(BeanMapTestCase.class);
+    }
+
+/*
+  note to self.  The getter and setter methods were generated by copying the
+  field declarations and using the following regular expression search and
+  replace:
+
+  From:
+        private \(.*\) some\(.*\);
+  To:
+        public \1 getSome\2Value() {
+            return some\2;
+        }
+        public void setSome\2Value(\1 value) {
+            some\2 = value;
+        }
+
+  Also note:  The sample keys and mappings were generated manually.
+*/
+
+
+    public static class BeanWithProperties implements Serializable {
+        private int someInt;
+        private long someLong;
+        private double someDouble;
+        private float someFloat;
+        private short someShort;
+        private byte someByte;
+        private char someChar;
+        private Integer someInteger;
+        private String someString;
+        private Object someObject;
+
+        public int getSomeIntValue() {
+            return someInt;
+        }
+        public void setSomeIntValue(final int value) {
+            someInt = value;
+        }
+
+        public long getSomeLongValue() {
+            return someLong;
+        }
+        public void setSomeLongValue(final long value) {
+            someLong = value;
+        }
+
+        public double getSomeDoubleValue() {
+            return someDouble;
+        }
+        public void setSomeDoubleValue(final double value) {
+            someDouble = value;
+        }
+
+        public float getSomeFloatValue() {
+            return someFloat;
+        }
+        public void setSomeFloatValue(final float value) {
+            someFloat = value;
+        }
+
+        public short getSomeShortValue() {
+            return someShort;
+        }
+        public void setSomeShortValue(final short value) {
+            someShort = value;
+        }
+
+        public byte getSomeByteValue() {
+            return someByte;
+        }
+        public void setSomeByteValue(final byte value) {
+            someByte = value;
+        }
+
+        public char getSomeCharValue() {
+            return someChar;
+        }
+        public void setSomeCharValue(final char value) {
+            someChar = value;
+        }
+
+        public String getSomeStringValue() {
+            return someString;
+        }
+        public void setSomeStringValue(final String value) {
+            someString = value;
+        }
+
+        public Integer getSomeIntegerValue() {
+            return someInteger;
+        }
+        public void setSomeIntegerValue(final Integer value) {
+            someInteger = value;
+        }
+
+        public Object getSomeObjectValue() {
+            return someObject;
+        }
+        public void setSomeObjectValue(final Object value) {
+            someObject = value;
+        }
+    }
+
+    public static class BeanThrowingExceptions extends BeanWithProperties {
+        private static final long serialVersionUID = 1L;
+        public void setValueThrowingException(final String value) {
+            throw new TestException();
+        }
+        public String getValueThrowingException() {
+            throw new TestException();
+        }
+    }
+
+    /**
+     * Exception for testing exception handling.
+     */
+    public static class TestException extends RuntimeException {
+        private static final long serialVersionUID = 1L;
+    }
+
+    // note to self.  The Sample keys were generated by copying the field
+    // declarations and using the following regular expression search and 
replace:
+    //
+    // From:
+    //    private \(.*\) some\(.*\);
+    // To:
+    //    "some\2Value",
+    //
+    // Then, I manually added the "class" key, which is a property that exists 
for
+    // all beans (and all objects for that matter.
+    @Override
+    public Object[] getSampleKeys() {
+        final Object[] keys = new Object[] {
+            "someIntValue",
+            "someLongValue",
+            "someDoubleValue",
+            "someFloatValue",
+            "someShortValue",
+            "someByteValue",
+            "someCharValue",
+            "someIntegerValue",
+            "someStringValue",
+            "someObjectValue",
+            "class",
+        };
+        return keys;
+    }
+
+    /**
+     *  An object value that will be stored in the bean map as a value.  Need
+     *  to save this externally so that we can make sure the object instances
+     *  are equivalent since getSampleValues() would otherwise construct a new
+     *  and different Object each time.
+     **/
+    private final Object objectInFullMap = new Object();
+
+    // note to self: the sample values were created manually
+    @Override
+    public Object[] getSampleValues() {
+        final Object[] values = new Object[] {
+            new Integer(1234),
+            new Long(1298341928234L),
+            new Double(123423.34),
+            new Float(1213332.12f),
+            new Short((short)134),
+            new Byte((byte)10),
+            new Character('a'),
+            new Integer(1432),
+            "SomeStringValue",
+            objectInFullMap,
+            BeanWithProperties.class,
+        };
+        return values;
+    }
+
+    @Override
+    public Object[] getNewSampleValues() {
+        final Object[] values = new Object[] {
+            new Integer(223),
+            new Long(23341928234L),
+            new Double(23423.34),
+            new Float(213332.12f),
+            new Short((short)234),
+            new Byte((byte)20),
+            new Character('b'),
+            new Integer(232),
+            "SomeNewStringValue",
+            new Object(),
+            null,
+        };
+        return values;
+    }
+
+    /**
+     * Values is a dead copy in BeanMap, so refresh each time.
+     */
+    @Override
+    public void verifyValues() {
+        values = map.values();
+        super.verifyValues();
+    }
+
+    /**
+     * The mappings in a BeanMap are fixed on the properties the underlying
+     * bean has.  Adding and removing mappings is not possible, thus this
+     * method is overridden to return false.
+     */
+    @Override
+    public boolean isPutAddSupported() {
+        return false;
+    }
+
+    /**
+     * The mappings in a BeanMap are fixed on the properties the underlying
+     * bean has.  Adding and removing mappings is not possible, thus this
+     * method is overridden to return false.
+     */
+    @Override
+    public boolean isRemoveSupported() {
+        return false;
+    }
+
+    @Override
+    public Map<Object, Object> makeFullMap() {
+        // note: These values must match (i.e. .equals() must return true)
+        // those returned from getSampleValues().
+        final BeanWithProperties bean = new BeanWithProperties();
+        bean.setSomeIntValue(1234);
+        bean.setSomeLongValue(1298341928234L);
+        bean.setSomeDoubleValue(123423.34);
+        bean.setSomeFloatValue(1213332.12f);
+        bean.setSomeShortValue((short)134);
+        bean.setSomeByteValue((byte)10);
+        bean.setSomeCharValue('a');
+        bean.setSomeIntegerValue(new Integer(1432));
+        bean.setSomeStringValue("SomeStringValue");
+        bean.setSomeObjectValue(objectInFullMap);
+        return new BeanMap(bean);
+    }
+
+    @Override
+    public Map<Object, Object> makeEmptyMap() {
+        return new BeanMap();
+    }
+
+    @Override
+    public String[] ignoredTests() {
+        // Ignore the serialization tests on collection views.
+        return new String[] {
+         "TestBeanMap.bulkTestMapEntrySet.testCanonicalEmptyCollectionExists",
+         "TestBeanMap.bulkTestMapEntrySet.testCanonicalFullCollectionExists",
+         "TestBeanMap.bulkTestMapKeySet.testCanonicalEmptyCollectionExists",
+         "TestBeanMap.bulkTestMapKeySet.testCanonicalFullCollectionExists",
+         "TestBeanMap.bulkTestMapValues.testCanonicalEmptyCollectionExists",
+         "TestBeanMap.bulkTestMapValues.testCanonicalFullCollectionExists",
+         "TestBeanMap.bulkTestMapEntrySet.testSimpleSerialization",
+         "TestBeanMap.bulkTestMapKeySet.testSimpleSerialization",
+         "TestBeanMap.bulkTestMapEntrySet.testSerializeDeserializeThenCompare",
+         "TestBeanMap.bulkTestMapKeySet.testSerializeDeserializeThenCompare"
+        };
+    }
+
+    /**
+     * Need to override this method because the "clear()" method on the bean
+     * map just returns the bean properties to their default states.  It does
+     * not actually remove the mappings as per the map contract.  The default
+     * testClear() methods checks that the clear method throws an
+     * UnsupportedOperationException since this class is not add/remove
+     * modifiable.  In our case though, we do not always throw that exception.
+     */
+    @Override
+    public void testMapClear() {
+        //TODO: make sure a call to BeanMap.clear returns the bean to its
+        //default initialization values.
+    }
+
+    /**
+     * Need to override this method because the "put()" method on the bean
+     * doesn't work for this type of Map.
+     */
+    @Override
+    public void testMapPut() {
+        // see testBeanMapPutAllWriteable
+    }
+
+    public void testBeanMapClone() {
+        final BeanMap map = (BeanMap)makeFullMap();
+        try {
+            final BeanMap map2 = (BeanMap)map.clone();
+
+            // make sure containsKey is working to verify the bean was cloned
+            // ok, and the read methods were properly initialized
+            final Object[] keys = getSampleKeys();
+            for (final Object key : keys) {
+                assertTrue("Cloned BeanMap should contain the same keys",
+                           map2.containsKey(key));
+            }
+        } catch (final CloneNotSupportedException exception) {
+            fail("BeanMap.clone() should not throw a " +
+                 "CloneNotSupportedException when clone should succeed.");
+        }
+    }
+
+    public void testBeanMapPutAllWriteable() {
+        final BeanMap map1 = (BeanMap)makeFullMap();
+        final BeanMap map2 = (BeanMap)makeFullMap();
+        map2.put("someIntValue", new Integer(0));
+        map1.putAllWriteable(map2);
+        assertEquals(map1.get("someIntValue"), new Integer(0));
+    }
+
+    public void testMethodAccessor() throws Exception {
+        final BeanMap map = (BeanMap) makeFullMap();
+        final Method method = 
BeanWithProperties.class.getDeclaredMethod("getSomeIntegerValue");
+        assertEquals(method, map.getReadMethod("someIntegerValue"));
+    }
+
+    public void testMethodMutator() throws Exception {
+        final BeanMap map = (BeanMap) makeFullMap();
+        final Method method = 
BeanWithProperties.class.getDeclaredMethod("setSomeIntegerValue", new Class[] 
{Integer.class});
+        assertEquals(method, map.getWriteMethod("someIntegerValue"));
+    }
+
+    /**
+     *  Test the default transformers using the getTypeTransformer() method
+     */
+    public void testGetTypeTransformerMethod() {
+        final BeanMap beanMap = new BeanMap();
+        assertEquals("Boolean.TYPE",   Boolean.TRUE,        
beanMap.getTypeTransformer(Boolean.TYPE).apply("true"));
+        assertEquals("Character.TYPE", new Character('B'),  
beanMap.getTypeTransformer(Character.TYPE).apply("BCD"));
+        assertEquals("Byte.TYPE",      new Byte((byte)1),   
beanMap.getTypeTransformer(Byte.TYPE).apply("1"));
+        assertEquals("Short.TYPE",     new Short((short)2), 
beanMap.getTypeTransformer(Short.TYPE).apply("2"));
+        assertEquals("Integer.TYPE",   new Integer(3),      
beanMap.getTypeTransformer(Integer.TYPE).apply("3"));
+        assertEquals("Long.TYPE",      new Long(4),         
beanMap.getTypeTransformer(Long.TYPE).apply("4"));
+        assertEquals("Float.TYPE",     new Float("5"),      
beanMap.getTypeTransformer(Float.TYPE).apply("5"));
+        assertEquals("Double.TYPE",    new Double("6"),     
beanMap.getTypeTransformer(Double.TYPE).apply("6"));
+    }
+
+    /**
+     * Test that the cause of exception thrown by a clone() is initialised.
+     */
+    public void testExceptionThrowFromClone() {
+
+        if (BeanUtilsTestCase.isPre14JVM()) {
+            System.out.println("testExceptionThrowFromClone() skipped on pre 
1.4 JVM");
+            return;
+        }
+
+        // Test cloning a non-public bean (instantiation exception)
+        try {
+            final Object bean = Jira87BeanFactory.createMappedPropertyBean();
+            final BeanMap map = new BeanMap(bean);
+            map.clone();
+            fail("Non-public bean clone() - expected 
CloneNotSupportedException");
+        } catch (final CloneNotSupportedException e) {
+            Throwable cause = null;
+            try {
+                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
+            } catch (final Exception e2) {
+                fail("Non-public bean - retrieving the cause threw " + e2);
+            }
+            assertNotNull("Non-public bean cause null", cause);
+            assertEquals("Non-public bean cause", 
IllegalAccessException.class, cause.getClass());
+        }
+
+        // Test cloning a bean that throws exception
+        try {
+            final BeanMap map = new BeanMap(new BeanThrowingExceptions());
+            map.clone();
+            fail("Setter Exception clone() - expected 
CloneNotSupportedException");
+        } catch (final CloneNotSupportedException e) {
+            Throwable cause = null;
+            try {
+                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
+            } catch (final Exception e2) {
+                fail("Setter Exception - retrieving the cause threw " + e2);
+            }
+            assertNotNull("Setter Exception cause null", cause);
+            assertEquals("Setter Exception cause", 
IllegalArgumentException.class, cause.getClass());
+        }
+    }
+
+    /**
+     * Test that the cause of exception thrown by clear() is initialised.
+     */
+    public void testExceptionThrowFromClear() {
+
+        if (BeanUtilsTestCase.isPre14JVM()) {
+            System.out.println("testExceptionThrowFromClear() skipped on pre 
1.4 JVM");
+            return;
+        }
+
+        try {
+            final Object bean = Jira87BeanFactory.createMappedPropertyBean();
+            final BeanMap map = new BeanMap(bean);
+            map.clear();
+            fail("clear() - expected UnsupportedOperationException");
+        } catch (final UnsupportedOperationException e) {
+            Throwable cause = null;
+            try {
+                cause = (Throwable)PropertyUtils.getProperty(e, "cause");
+            } catch (final Exception e2) {
+                fail("Retrieving the cause threw " + e2);
+            }
+            assertNotNull("Cause null", cause);
+            assertEquals("Cause", IllegalAccessException.class, 
cause.getClass());
+        }
+    }
+
+    /**
+     * Test that the cause of exception thrown by put() is initialized.
+     */
+    public void testExceptionThrowFromPut() {
+
+        if (BeanUtilsTestCase.isPre14JVM()) {
+            System.out.println("testExceptionThrowFromPut() skipped on pre 1.4 
JVM");
+            return;
+        }
+
+        try {
+            final Map<Object, Object> map = new BeanMap(new 
BeanThrowingExceptions());
+            map.put("valueThrowingException", "value");
+            fail("Setter exception - expected IllegalArgumentException");
+        } catch (final IllegalArgumentException e) {
+            Throwable cause1 = null;
+            Throwable cause2 = null;
+            try {
+                cause1 = (Throwable)PropertyUtils.getProperty(e, "cause");
+                cause2 = (Throwable)PropertyUtils.getProperty(e, 
"cause.cause");
+            } catch (final Exception e2) {
+                fail("Setter exception - retrieving the cause threw " + e2);
+            }
+            assertNotNull("Setter exception cause 1 null", cause1);
+            assertEquals("Setter exception cause 1", 
InvocationTargetException.class, cause1.getClass());
+            assertNotNull("Setter exception cause 2 null", cause2);
+            assertEquals("Setter exception cause 2", TestException.class, 
cause2.getClass());
+        }
+    }
+}

Reply via email to