blob: e91ec93fa27b9f8645977a36cebc52391a51e4b7 [file] [log] [blame]
package org.apache.commons.jcs3.utils.config;
/*
* 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.
*/
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Properties;
import org.apache.commons.jcs3.log.Log;
import org.apache.commons.jcs3.log.LogManager;
/**
* This class is based on the log4j class org.apache.log4j.config.PropertySetter that was made by
* Anders Kristensen
* <p>
* General purpose Object property setter. Clients repeatedly invokes {@link #setProperty
* setProperty(name,value)} in order to invoke setters on the Object specified in the constructor.
* This class relies on the JavaBeans {@link Introspector}to analyze the given Object Class using
* reflection.
* <p>
* Usage:
*
* <pre>
* PropertySetter ps = new PropertySetter( anObject );
* ps.set( &quot;name&quot;, &quot;Joe&quot; );
* ps.set( &quot;age&quot;, &quot;32&quot; );
* ps.set( &quot;isMale&quot;, &quot;true&quot; );
* </pre>
*
* will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and setMale(true) if
* such methods exist with those signatures. Otherwise an {@link IntrospectionException}are thrown.
*/
public class PropertySetter
{
/** Logger */
private static final Log log = LogManager.getLog( PropertySetter.class );
/** Description of the Field */
private final Object obj;
/** Description of the Field */
private PropertyDescriptor[] props;
/**
* Create a new PropertySetter for the specified Object. This is done in preparation for invoking
* {@link #setProperty}one or more times.
* @param obj the object for which to set properties
*/
public PropertySetter( Object obj )
{
this.obj = obj;
}
/**
* Uses JavaBeans {@link Introspector}to compute setters of object to be configured.
*/
protected void introspect()
{
try
{
BeanInfo bi = Introspector.getBeanInfo( obj.getClass() );
props = bi.getPropertyDescriptors();
}
catch ( IntrospectionException ex )
{
log.error( "Failed to introspect {0}", obj, ex );
props = new PropertyDescriptor[0];
}
}
/**
* Set the properties of an object passed as a parameter in one go. The <code>properties</code>
* are parsed relative to a <code>prefix</code>.
* <p>
* @param obj The object to configure.
* @param properties A java.util.Properties containing keys and values.
* @param prefix Only keys having the specified prefix will be set.
*/
public static void setProperties( Object obj, Properties properties, String prefix )
{
new PropertySetter( obj ).setProperties( properties, prefix );
}
/**
* Set the properties for the object that match the <code>prefix</code> passed as parameter.
* <p>
* @param properties The new properties value
* @param prefix The new properties value
*/
public void setProperties( Properties properties, String prefix )
{
int len = prefix.length();
for ( Enumeration<?> e = properties.propertyNames(); e.hasMoreElements(); )
{
String key = (String) e.nextElement();
// handle only properties that start with the desired prefix.
if ( key.startsWith( prefix ) )
{
// ignore key if it contains dots after the prefix
if ( key.indexOf( '.', len + 1 ) > 0 )
{
//System.err.println("----------Ignoring---["+key
// +"], prefix=["+prefix+"].");
continue;
}
String value = OptionConverter.findAndSubst( key, properties );
key = key.substring( len );
setProperty( key, value );
}
}
}
/**
* Set a property on this PropertySetter's Object. If successful, this method will invoke a
* setter method on the underlying Object. The setter is the one for the specified property name
* and the value is determined partly from the setter argument type and partly from the value
* specified in the call to this method.
* <p>
* If the setter expects a String no conversion is necessary. If it expects an int, then an
* attempt is made to convert 'value' to an int using Integer.valueOf(value). If the setter expects
* a boolean, the conversion is by Boolean.valueOf(value).
* @param name name of the property
* @param value String value of the property
*/
public void setProperty( String name, String value )
{
if ( value == null )
{
return;
}
name = Introspector.decapitalize( name );
PropertyDescriptor prop = getPropertyDescriptor( name );
//log.debug("---------Key: "+name+", type="+prop.getPropertyType());
if ( prop == null )
{
log.warn( "No such property [{0}] in {1}.", name, obj.getClass().getName() );
}
else
{
try
{
setProperty( prop, name, value );
}
catch ( PropertySetterException ex )
{
log.warn( "Failed to set property {0} to value \"{1}\".", name, value, ex );
}
}
}
/**
* Set the named property given a {@link PropertyDescriptor}.
* @param prop A PropertyDescriptor describing the characteristics of the property to set.
* @param name The named of the property to set.
* @param value The value of the property.
* @throws PropertySetterException
*/
public void setProperty( PropertyDescriptor prop, String name, String value )
throws PropertySetterException
{
Method setter = prop.getWriteMethod();
if ( setter == null )
{
throw new PropertySetterException( "No setter for property" );
}
Class<?>[] paramTypes = setter.getParameterTypes();
if ( paramTypes.length != 1 )
{
throw new PropertySetterException( "#params for setter != 1" );
}
Object arg;
try
{
arg = convertArg( value, paramTypes[0] );
}
catch ( Throwable t )
{
throw new PropertySetterException( "Conversion to type [" + paramTypes[0] + "] failed. Reason: " + t );
}
if ( arg == null )
{
throw new PropertySetterException( "Conversion to type [" + paramTypes[0] + "] failed." );
}
log.debug( "Setting property [{0}] to [{1}].", name, arg );
try
{
setter.invoke( obj, new Object[] { arg } );
}
catch ( Exception ex )
{
throw new PropertySetterException( ex );
}
}
/**
* Convert <code>val</code> a String parameter to an object of a given type.
* @param val
* @param type
* @return Object
*/
protected Object convertArg( String val, Class<?> type )
{
if ( val == null )
{
return null;
}
String v = val.trim();
if ( String.class.isAssignableFrom( type ) )
{
return val;
}
else if ( Integer.TYPE.isAssignableFrom( type ) )
{
return Integer.valueOf( v );
}
else if ( Long.TYPE.isAssignableFrom( type ) )
{
return Long.valueOf( v );
}
else if ( Boolean.TYPE.isAssignableFrom( type ) )
{
if ( "true".equalsIgnoreCase( v ) )
{
return Boolean.TRUE;
}
else if ( "false".equalsIgnoreCase( v ) )
{
return Boolean.FALSE;
}
}
else if( type.isEnum() )
{
Enum<?> en = Enum.valueOf(type.asSubclass(Enum.class), v );
return en;
}
else if ( File.class.isAssignableFrom( type ) )
{
return new File( v );
}
return null;
}
/**
* Gets the propertyDescriptor attribute of the PropertySetter object
* @param name
* @return The propertyDescriptor value
*/
protected PropertyDescriptor getPropertyDescriptor( String name )
{
if ( props == null )
{
introspect();
}
for ( int i = 0; i < props.length; i++ )
{
if ( name.equals( props[i].getName() ) )
{
return props[i];
}
}
return null;
}
}