/*
* PropertiesBean.java: a "serializable" Java Bean that uses a
* java.util.Properties backend.
* :noTabs=false:
*
* Copyright (C) 2006 Marcelo Vanzin
*
* This library is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as published
* by the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.gjt.sp.util;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Properties;
import java.util.StringTokenizer;
/**
* A "java bean" that can serialize itself into a java.util.Properties
* instance. For the serialization, the class uses the java beans
* instrospection mechanism to figure out the class's available
* properties, and saves all the properties as strings in the properties
* object.
*
* <p>Properties are saved based on a "root", which is set up during the
* instantiation of the object. The properties will be set as
* <code>root.property_name</code>.</p>
*
* <p>Only native types (boolean, char, double, float, int, long, short),
* Strings, and arrays of those types are supported. Also, nested
* beans are not supported presently.</p>
*
* @author Marcelo Vanzin
* @since jEdit 4.3pre7
*/
public abstract class PropertiesBean
{
// Constructors
/**
* Creates a new instance with the given root and the default array
* separator char (':').
*
* @param root A non-null string that will be the "root" of the
* serialized properties.
*/
protected PropertiesBean(String root)
{
this(root, ':');
}
/**
* Creates a new instance with the given root and the given array
* separator character.
*
* @param root A non-null string that will be the "root" of the
* serialized properties.
* @param arraysep A character that will be used to define the
* separator of elements of an array property.
*/
protected PropertiesBean(String root, char arraysep)
{
if (root == null)
throw new IllegalArgumentException("root cannot be null");
this.root = root;
this.arraysep = arraysep;
}
// Public methods
/**
* Loads the bean's properties from the given object.
*/
public void load(Properties p)
{
try
{
PropertyDescriptor[] _props = getPropertyDescriptors();
for (PropertyDescriptor prop : _props)
{
if ("class".equals(prop.getName()))
continue;
Method _set = prop.getWriteMethod();
if (_set != null)
{
String _pname = root + "." + prop.getName();
Object _val = p.getProperty(_pname);
if (_val != null)
_val = parse((String) _val, prop.getPropertyType());
try
{
_set.invoke(this, _val);
}
catch (IllegalArgumentException iae)
{
/* Ignore these. */
}
}
}
}
catch (Exception e)
{
// These exceptions shouldn't occur during normal runtime,
// so we catch them and print an error message. Users of this
// class should fix these before releasing the code.
Log.log(Log.ERROR, this, e);
}
}
/**
* Saves the bean's properties into the given object.
*/
public void save(Properties p)
{
try
{
PropertyDescriptor[] _props = getPropertyDescriptors();
for (PropertyDescriptor prop : _props)
{
if ("class".equals(prop.getName())) continue;
Method _get = prop.getReadMethod();
if (_get != null)
{
Object _val = _get.invoke(this);
String _pname = root + "." + prop.getName();
if (_val != null) p.setProperty(_pname, encode(_val));
else p.remove(_pname);
}
}
}
catch (Exception e)
{
// These exceptions shouldn't occur during normal runtime,
// so we catch them and print an error message. Users of this
// class should fix these before releasing the code.
Log.log(Log.ERROR, this, e);
}
}
/**
* Cleans the entries related to this object from the given object.
*/
public void clean(Properties p)
{
try
{
PropertyDescriptor[] _props = getPropertyDescriptors();
for (PropertyDescriptor prop : _props)
{
if ("class".equals(prop.getName())) continue;
String _pname = root + "." + prop.getName();
p.remove(_pname);
}
}
catch (Exception e)
{
// These exceptions shouldn't occur during normal runtime,
// so we catch them and print an error message. Users of this
// class should fix these before releasing the code.
Log.log(Log.ERROR, this, e);
}
}
// Private methods
private PropertyDescriptor[] getPropertyDescriptors()
throws IntrospectionException
{
BeanInfo _info = Introspector.getBeanInfo(getClass());
return _info.getPropertyDescriptors();
}
private String encode(Object value)
{
Class<?> _class = value.getClass();
if (_class.isArray())
{
StringBuilder _val = new StringBuilder();
int _len = Array.getLength(value);
for (int i = 0; i < _len; i++)
{
String _str = encode(Array.get(value, i));
if (_str == null)
return null;
_val.append(_str);
if (i < _len - 1)
_val.append(arraysep);
}
return _val.toString();
}
else
{
// just make sure it's a supported type.
if (_class != Boolean.class && _class != Boolean.TYPE
&& _class != Character.class && _class != Character.TYPE
&& _class != Double.class && _class != Double.TYPE
&& _class != Float.class && _class != Float.TYPE
&& _class != Integer.class && _class != Integer.TYPE
&& _class != Long.class && _class != Long.TYPE
&& _class != Short.class && _class != Short.TYPE
&& _class != String.class)
{
Log.log(Log.WARNING, this, "unsupported type: " + _class.getName());
return null;
}
return value.toString();
}
}
private Object parse(String value, Class<?> _class)
{
Object _ret = null;
if (_class.isArray())
{
StringTokenizer st = new StringTokenizer(value, String.valueOf(arraysep));
Class<?> _type = _class.getComponentType();
_ret = Array.newInstance(_type, st.countTokens());
int _cnt = st.countTokens();
for (int i = 0; i < _cnt; i++)
{
Object _val = parse(st.nextToken(), _type);
if (_val == null)
return null;
Array.set(_ret, i, _val);
}
}
else
{
if (_class == Boolean.class || _class == Boolean.TYPE)
_ret = Boolean.valueOf(value);
else if (_class == Character.class || _class == Character.TYPE)
_ret = Character.valueOf(value.charAt(0));
else if (_class == Double.class || _class == Double.TYPE)
_ret = Double.valueOf(value);
else if (_class == Float.class || _class == Float.TYPE)
_ret = Float.valueOf(value);
else if (_class == Integer.class || _class == Integer.TYPE)
_ret = Integer.valueOf(value);
else if (_class == Long.class || _class == Long.TYPE)
_ret = Long.valueOf(value);
else if (_class == Short.class || _class == Short.TYPE)
_ret = Short.valueOf(value);
else if (_class == String.class)
_ret = value;
else
Log.log(Log.WARNING, this, "unsupported type: " + _class.getName());
}
return _ret;
}
// Instance variables
private final char arraysep;
private final String root;
}