2
package org.crosswire.common.config.choices;
4
import java.beans.IntrospectionException;
5
import java.lang.reflect.InvocationTargetException;
6
import java.lang.reflect.Method;
8
import org.apache.log4j.Logger;
9
import org.crosswire.common.config.Choice;
10
import org.crosswire.common.util.Convert;
11
import org.crosswire.common.util.UserLevel;
12
import org.jdom.Element;
15
* A choice that will work using reflection on a wide variety of
17
* The action paramter with which this class is constructed must be of the form:
18
* <code>full.package.name.ClassName.StaticProperty</code>
19
* Where StaticProperty is a name to which we can prepend get OR set and then
20
* use introspection to find a methods where the setter takes a single parameter
21
* and the getter takes no paramters, but returns something of the same type.
22
* If there are several parameter types that match the above definitions then
23
* we take String in preference to all others. Other parameter types match in
24
* an undefined manner. The types that are currently supported include:
31
* <p><table border='1' cellPadding='3' cellSpacing='0'>
32
* <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
34
* Distribution Licence:<br />
35
* JSword is free software; you can redistribute it
36
* and/or modify it under the terms of the GNU General Public License,
37
* version 2 as published by the Free Software Foundation.<br />
38
* This program is distributed in the hope that it will be useful,
39
* but WITHOUT ANY WARRANTY; without even the implied warranty of
40
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41
* General Public License for more details.<br />
42
* The License is available on the internet
43
* <a href='http://www.gnu.org/copyleft/gpl.html'>here</a>, or by writing to:
44
* Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
45
* MA 02111-1307, USA<br />
46
* The copyright to this program is held by it's authors.
47
* </font></td></tr></table>
49
* @author Joe Walker [joe at eireneh dot com]
52
public class StaticReflectiveChoice implements Choice
55
* Construct how this choice will act when called to configure itself
57
public StaticReflectiveChoice(Element config) throws ClassNotFoundException, IntrospectionException
59
// The important 3 things saying what we update and how we describe ourselves
60
String clazzname = getSubElementText(config, "class");
61
String propertyname = getSubElementText(config, "property");
62
helptext = getSubElementText(config, "help");
64
// 2 optional config attrubites
65
level = UserLevel.forName(config.getAttributeValue("level"));
66
String priorityname = config.getAttributeValue("priority");
67
if (priorityname == null)
68
priority = Choice.PRIORITY_NORMAL;
70
priority = Integer.parseInt(priorityname);
72
data = getSubElementText(config, "data");
74
// Find an appropriate class
75
clazz = Class.forName(clazzname);
77
// Find an appropriate method
78
boolean found = false;
79
for (int i=0; i<types.length && !found; i++)
81
found = setMethods(propertyname, types[i]);
85
throw new IntrospectionException("No methods found for "+clazzname+".[g|s]et"+propertyname);
89
* Get the text in a sub element
91
private String getSubElementText(Element config, String child)
93
Element childele = config.getChild(child);
97
return childele.getTextTrim();
101
* Only call this method from within the ctor or you will get all sorts of
102
* synchronization errors.
104
private boolean setMethods(String propertyname, TypeConverter test)
108
Method temp_setter = clazz.getMethod("set"+propertyname, new Class[] { test.getTypeToConvert() });
109
Method temp_getter = null;
112
temp_getter = clazz.getMethod("get"+propertyname, new Class[0]);
116
temp_getter = clazz.getMethod("is"+propertyname, new Class[0]);
119
if (temp_getter.getReturnType() == test.getTypeToConvert())
121
setter = temp_setter;
122
getter = temp_getter;
127
log.debug("Not using "+propertyname+" from "+clazz.getName()+" because the return type of the getter is not "+test.getTypeToConvert().getName());
130
catch (NoSuchMethodException ex)
137
* @see org.crosswire.common.config.Choice#getString()
139
public String getString()
143
Object retval = getter.invoke(null, new Object[0]);
144
return type.toString(retval);
146
catch (IllegalAccessException ex)
148
log.error("Illegal access getting value from "+clazz.getName()+"."+getter.getName(), ex);
151
catch (InvocationTargetException ex)
153
log.error("Failed to get value from "+clazz.getName()+"."+getter.getName(), ex);
159
* @see org.crosswire.common.config.Choice#setString(String)
161
public void setString(String value) throws Exception
163
setter.invoke(null, new Object[] { type.fromString(value) });
169
public String getHelpText()
175
* Override this to check and note any change
177
public String getType()
179
return type.getTypeName();
183
* This method is used to configure a the type selected above.
184
* The object returned will depend on the type of editor selected.
185
* For example an editor of type "options" may need a String array.
186
* @return a configuration parameter for the type
188
public Object getTypeOptions()
190
return type.getTypeOptions();
196
public UserLevel getUserLevel()
202
* The priority which which we configure this item
203
* @return A priority level
205
public int priority()
211
* Is this Choice OK to write out to a file, or should we use settings
212
* in this run of the program, but forget them for next time. A
213
* typical use of this is for password configuration.
214
* @return True if it is safe to store the value in a config file.
216
public boolean isSaveable()
222
* Do we need to restart the program in order for this change to have
224
* @return True if a restart is required
226
public boolean requiresRestart()
232
* A class to convert between strings and objects of a type
234
public abstract class TypeConverter
236
public abstract Class getTypeToConvert();
237
public abstract String getTypeName();
238
public abstract Object getTypeOptions();
239
public abstract String toString(Object orig);
240
public abstract Object fromString(String orig);
244
* A class to convert between strings and objects of a type
246
public class StringTypeConverter extends TypeConverter
248
public Class getTypeToConvert()
253
public String getTypeName()
258
public Object getTypeOptions()
263
public String toString(Object orig)
265
return (String) orig;
268
public Object fromString(String orig)
275
* A class to convert between strings and objects of a type
277
public class IntegerTypeConverter extends TypeConverter
279
public Class getTypeToConvert()
284
public String getTypeName()
289
public Object getTypeOptions()
294
public String toString(Object orig)
296
return Convert.int2String(((Integer) orig).intValue());
299
public Object fromString(String orig)
301
return new Integer(Convert.string2Int(orig));
306
* A class to convert between strings and objects of a type
308
public class StringArrayTypeConverter extends TypeConverter
310
public Class getTypeToConvert()
312
return String[].class;
315
public String getTypeName()
320
public Object getTypeOptions()
325
public String toString(Object orig)
327
return Convert.stringArray2String(((String[]) orig));
330
public Object fromString(String orig)
332
return Convert.string2StringArray(orig);
337
* A class to convert between strings and objects of a type
339
public class BooleanTypeConverter extends TypeConverter
341
public Class getTypeToConvert()
346
public String getTypeName()
351
public Object getTypeOptions()
356
public String toString(Object orig)
358
return Convert.boolean2String(((Boolean) orig).booleanValue());
361
public Object fromString(String orig)
363
return new Boolean(Convert.string2Boolean(orig));
368
* A class to convert between strings and objects of a type
370
public class UserLevelTypeConverter extends TypeConverter
372
public Class getTypeToConvert()
374
return UserLevel.class;
377
public String getTypeName()
382
public Object getTypeOptions()
384
return UserLevel.getUserLevels();
387
public String toString(Object orig)
389
return ((UserLevel) orig).getName();
392
public Object fromString(String orig)
394
return UserLevel.forName(orig);
399
* A class to convert between strings and objects of a type
401
public class ClassTypeConverter extends TypeConverter
403
public Class getTypeToConvert()
408
public String getTypeName()
413
public Object getTypeOptions()
418
public String toString(Object orig)
420
return ((Class) orig).getName();
423
public Object fromString(String orig)
427
return Class.forName(orig);
429
catch (ClassNotFoundException ex)
431
log.warn("Class not found: "+orig, ex);
438
* The converters that we know about.
440
private TypeConverter[] types = new TypeConverter[]
442
new StringTypeConverter(),
443
new StringArrayTypeConverter(),
444
new BooleanTypeConverter(),
445
new UserLevelTypeConverter(),
446
new IntegerTypeConverter(),
447
new ClassTypeConverter(),
451
* The method to call to get the value
453
private Method getter;
456
* The method to call to set the value
458
private Method setter;
461
* The class we are going to call a static method on
466
* The parameter type that we convert to and from
468
private TypeConverter type;
471
* The help text (tooltip) for this item
473
private String helptext;
478
private UserLevel level;
481
* Some options require some extra data in the config file
486
* The priority of this config level
488
private int priority;
493
protected static Logger log = Logger.getLogger(StaticReflectiveChoice.class);