/*
* Copyright 1998-2012 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.util.prefs.ui;
import ucar.util.prefs.PersistenceManager;
import java.beans.*;
import java.lang.reflect.*;
import java.util.*;
/**
* Manages mapping of PrefPanel fields to java beans.
*
* @author John Caron
*/
public class PersistentBean implements PersistenceManager {
private BeanMap beanMap;
private boolean debugBean = false;
public PersistentBean( Object bean) {
beanMap = new BeanMap( bean);
}
// public methods for PersistenceManager interface
public Object getObject(String name) {
return beanMap.getObject( name);
}
public void putObject(String name, Object value) {
beanMap.putObject( name, value);
}
public void addPreferenceChangeListener(java.util.prefs.PreferenceChangeListener pcl) { }
public String get(String key, String def) {
Object value = getObject( key);
return (value == null) ? def : value.toString();
}
public void put(String key, String value) {
putObject( key, value);
}
public double getDouble(String key, double def) {
Object value = getObject( key);
return (value == null) ? def : ((Number) value).doubleValue();
}
public void putDouble(String key, double value) {
putObject( key, new Double(value));
}
public boolean getBoolean(String key, boolean def) {
Object value = getObject( key);
return (value == null) ? def : (Boolean) value;
}
public void putBoolean(String key, boolean value) {
putObject( key, Boolean.valueOf(value));
}
public int getInt(String key, int def) {
Object value = getObject( key);
return (value == null) ? def : ((Number) value).intValue();
}
public void putInt(String key, int value) {
putObject( key, new Integer(value));
}
public long getLong(String key, long def) {
Object value = getObject( key);
return (value == null) ? def : ((Number) value).longValue();
}
public void putLong(String key, long value) {
putObject( key, new Long(value));
}
public java.util.List getList(String key, java.util.List def) {
Object value = getObject( key);
return (value == null) ? def : (List) value;
}
public void putList(String key, java.util.List value) {
putObject( key, value);
}
// one for each bean; handles nested beans
private class BeanMap {
private Object bean;
private PropertyMap pmap;
private Map<String, BeanMap> beanMaps = new HashMap<>(); // nested BeanMap
private Object[] args = new Object[1];
BeanMap( Object bean){
this.bean = bean;
this.pmap = PropertyMap.getParser( bean.getClass());
}
private ProxyProp getPropertyDescriptor(String name) {
// non-nested
PropertyDescriptor prop = pmap.findProperty(name);
if (prop != null)
return new ProxyProp( prop, null, null);
// see if its nested
int pos = name.indexOf(".");
if (pos < 0)
throw new IllegalArgumentException("PersistentBean: no property named "+name);
// break out first bean name
String parentName = name.substring(0, pos);
String childrenName = name.substring(pos+1);
prop = pmap.findProperty( parentName);
if (prop == null)
throw new IllegalArgumentException("PersistentBean: no property named "+parentName);
BeanMap nested = beanMaps.get( parentName);
if (nested == null) {
// first time - create a nested BeanMap
Object bean = getObject( parentName);
if (bean == null) {
// create a new one
bean = createObject(prop);
putObject(parentName, bean);
}
nested = new BeanMap(bean);
beanMaps.put(parentName, nested);
}
return new ProxyProp( null, nested, childrenName);
}
private Object createObject(PropertyDescriptor prop) {
Class propClass = prop.getPropertyType();
try {
return propClass.newInstance();
} catch (Exception ee) {
ee.printStackTrace();
// System.out.println("PersistentBean error createObject: "+prop.getName()+" "+ee.getMessage());
throw new IllegalArgumentException("PersistentBean error createObject: "+prop.getName()+" "+ee.getMessage());
}
}
public Object getObject(String name) {
if (debugBean) System.out.println( "PersistentBean read "+name);
ProxyProp proxy = getPropertyDescriptor(name);
if (proxy.prop == null) {
return proxy.nested.getObject(proxy.childrenName);
}
PropertyDescriptor prop = proxy.prop;
Object value = null;
try {
Method m = prop.getReadMethod();
if (m == null) {
System.out.println("PersistentBean no read method for: "+name);
return null;
}
value = m.invoke( bean, (Object []) null);
} catch (InvocationTargetException ee) {
System.out.println("PersistentBean error read: "+name+" "+ee.getCause());
ee.getCause().printStackTrace();
} catch (Exception ee) {
System.out.println("PersistentBean error read: "+name+" "+ee);
ee.printStackTrace();
}
return value;
}
public void putObject(String name, Object value) {
if (debugBean) System.out.println( "PersistentBean write "+name + " = "+value +" "+value.getClass().getName());
ProxyProp proxy = getPropertyDescriptor(name);
if (proxy.prop == null) {
proxy.nested.putObject(proxy.childrenName, value);
return;
}
PropertyDescriptor prop = proxy.prop;
args[0] = value;
try {
Method m = prop.getWriteMethod();
if (m == null) {
System.out.println("PersistentBean no write method for: "+name);
return;
}
m.invoke( bean, args);
} catch (InvocationTargetException ee) {
System.out.println("PersistentBean error write: "+name+" "+ee.getCause());
ee.getCause().printStackTrace();
} catch (Exception ee) {
System.out.println("PersistentBean error write: "+name+" "+ee);
ee.printStackTrace();
}
}
}
// helper class
private static class ProxyProp {
private PropertyDescriptor prop;
private BeanMap nested;
private String childrenName;
ProxyProp(PropertyDescriptor prop, BeanMap nested, String childrenName) {
this.prop = prop;
this.nested = nested;
this.childrenName = childrenName;
}
}
// one for each class
private static class PropertyMap {
private static boolean debugBeanParser = false, debugBeanParserDetail = false;
private static Map<Class,PropertyMap> parsers = new HashMap<>();
static PropertyMap getParser( Class beanClass) {
PropertyMap parser;
if (null == (parser = parsers.get( beanClass))) {
parser = new PropertyMap( beanClass);
parsers.put( beanClass, parser);
}
return parser;
}
private Map<String,PropertyDescriptor> properties = new LinkedHashMap<>();
PropertyMap( Class beanClass) {
// get bean info
BeanInfo info;
try {
info = Introspector.getBeanInfo(beanClass, Object.class);
} catch (IntrospectionException e) {
e.printStackTrace();
return;
}
if (debugBeanParser)
System.out.println( "Bean "+beanClass.getName());
// properties must have read method
PropertyDescriptor[] pds = info.getPropertyDescriptors();
for (PropertyDescriptor prop : pds) {
Class propClass = prop.getPropertyType();
if ((prop.getReadMethod() != null)) { // && (prop.getWriteMethod() != null)) {
properties.put(prop.getName(), prop);
if (debugBeanParser) System.out.println(" read/write property " + prop.getName() + " " +
propClass.getName() + " prim= " + propClass.isPrimitive());
/* if (!propClass.isPrimitive() && !(propClass == java.lang.String.class)) {
PropertyMap nestedParser = PropertyMap.getParser(propClass);
for (Iterator iter = nestedParser.getProperties(); iter.hasNext(); ) {
PropertyDescriptor nestedProp = (PropertyDescriptor)iter.next();
properties.put( prop.getName()+"."+nestedProp.getName(), nestedProp);
if (debugBeanParser) System.out.println( " -added property "+
prop.getName()+"."+nestedProp.getName());
}
} */
}
}
if (debugBeanParserDetail) {
System.out.println( " Properties:");
for (PropertyDescriptor pd : pds) {
String name = pd.getName();
Class type = pd.getPropertyType();
Method rm = pd.getReadMethod();
Method wm = pd.getWriteMethod();
System.out.println(" " + name + " " + type.getName() + " read= " + rm + " write= " + wm + " " + pd.isPreferred());
System.out.println(" displayname= " + pd.getDisplayName());
}
}
}
Iterator<PropertyDescriptor> getProperties() { return properties.values().iterator(); }
PropertyDescriptor findProperty( String name) {
return properties.get(name);
}
}
}