~sword-devel/jsword/trunk

« back to all changes in this revision

Viewing changes to jsword/java/common/org/crosswire/common/config/choices/StaticReflectiveChoice.java

  • Committer: joe
  • Date: 2002-10-08 21:36:18 UTC
  • Revision ID: svn-v4:a88caf3b-7e0a-0410-8d0d-cecb45342206:trunk:80
big config and comment update

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
 
2
 
package org.crosswire.common.config.choices;
3
 
 
4
 
import java.beans.IntrospectionException;
5
 
import java.lang.reflect.InvocationTargetException;
6
 
import java.lang.reflect.Method;
7
 
 
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;
13
 
 
14
 
/**
15
 
 * A choice that will work using reflection on a wide variety of
16
 
 * configurable items.
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:
25
 
 * <li>String
26
 
 * <li>String[]
27
 
 * <li>Boolean
28
 
 * <li>Integer
29
 
 * <li>Class
30
 
 * 
31
 
 * <p><table border='1' cellPadding='3' cellSpacing='0'>
32
 
 * <tr><td bgColor='white' class='TableRowColor'><font size='-7'>
33
 
 *
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>
48
 
 * @see docs.Licence
49
 
 * @author Joe Walker [joe at eireneh dot com]
50
 
 * @version $Id$
51
 
 */
52
 
public class StaticReflectiveChoice implements Choice
53
 
{
54
 
    /**
55
 
     * Construct how this choice will act when called to configure itself
56
 
     */
57
 
    public StaticReflectiveChoice(Element config) throws ClassNotFoundException, IntrospectionException
58
 
    {
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");
63
 
 
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;
69
 
        else
70
 
            priority = Integer.parseInt(priorityname);
71
 
 
72
 
        data = getSubElementText(config, "data");
73
 
 
74
 
        // Find an appropriate class        
75
 
        clazz = Class.forName(clazzname);
76
 
 
77
 
        // Find an appropriate method
78
 
        boolean found = false;
79
 
        for (int i=0; i<types.length && !found; i++)
80
 
        {
81
 
            found = setMethods(propertyname, types[i]);
82
 
        }
83
 
        
84
 
        if (!found)
85
 
            throw new IntrospectionException("No methods found for "+clazzname+".[g|s]et"+propertyname);
86
 
    }
87
 
    
88
 
    /**
89
 
     * Get the text in a sub element
90
 
     */
91
 
    private String getSubElementText(Element config, String child)
92
 
    {
93
 
        Element childele = config.getChild(child);
94
 
        if (childele == null)
95
 
            return null;
96
 
 
97
 
        return childele.getTextTrim();
98
 
    }
99
 
 
100
 
    /**
101
 
     * Only call this method from within the ctor or you will get all sorts of
102
 
     * synchronization errors.
103
 
     */
104
 
    private boolean setMethods(String propertyname, TypeConverter test)
105
 
    {
106
 
        try
107
 
        {
108
 
            Method temp_setter = clazz.getMethod("set"+propertyname, new Class[] { test.getTypeToConvert() });
109
 
            Method temp_getter = null;
110
 
            try
111
 
            {
112
 
                temp_getter = clazz.getMethod("get"+propertyname, new Class[0]);
113
 
            }
114
 
            catch (Exception ex)
115
 
            {
116
 
                temp_getter = clazz.getMethod("is"+propertyname, new Class[0]);
117
 
            }
118
 
 
119
 
            if (temp_getter.getReturnType() == test.getTypeToConvert())
120
 
            {
121
 
                setter = temp_setter;
122
 
                getter = temp_getter;
123
 
                type = test;
124
 
                return true;
125
 
            }
126
 
 
127
 
            log.debug("Not using "+propertyname+" from "+clazz.getName()+" because the return type of the getter is not "+test.getTypeToConvert().getName());
128
 
            return false;
129
 
        }
130
 
        catch (NoSuchMethodException ex)
131
 
        {
132
 
            return false;
133
 
        }
134
 
    }
135
 
 
136
 
    /**
137
 
     * @see org.crosswire.common.config.Choice#getString()
138
 
     */
139
 
    public String getString()
140
 
    {
141
 
        try
142
 
        {
143
 
            Object retval = getter.invoke(null, new Object[0]);
144
 
            return type.toString(retval);
145
 
        }
146
 
        catch (IllegalAccessException ex)
147
 
        {
148
 
            log.error("Illegal access getting value from "+clazz.getName()+"."+getter.getName(), ex);
149
 
            return "";
150
 
        }
151
 
        catch (InvocationTargetException ex)
152
 
        {
153
 
            log.error("Failed to get value from "+clazz.getName()+"."+getter.getName(), ex);
154
 
            return "";
155
 
        }
156
 
    }
157
 
 
158
 
    /**
159
 
     * @see org.crosswire.common.config.Choice#setString(String)
160
 
     */
161
 
    public void setString(String value) throws Exception
162
 
    {
163
 
        setter.invoke(null, new Object[] { type.fromString(value) });
164
 
    }
165
 
 
166
 
    /**
167
 
     * Help text
168
 
     */
169
 
    public String getHelpText()
170
 
    {
171
 
        return helptext;
172
 
    }
173
 
 
174
 
    /**
175
 
     * Override this to check and note any change
176
 
     */
177
 
    public String getType()
178
 
    {
179
 
        return type.getTypeName();
180
 
    }
181
 
 
182
 
    /**
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
187
 
     */
188
 
    public Object getTypeOptions()
189
 
    {
190
 
        return type.getTypeOptions();
191
 
    }
192
 
 
193
 
    /**
194
 
     * The UserLevel
195
 
     */
196
 
    public UserLevel getUserLevel()
197
 
    {
198
 
        return level;
199
 
    }
200
 
 
201
 
    /**
202
 
     * The priority which which we configure this item
203
 
     * @return A priority level
204
 
     */
205
 
    public int priority()
206
 
    {
207
 
        return priority;
208
 
    }
209
 
 
210
 
    /**
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.
215
 
     */
216
 
    public boolean isSaveable()
217
 
    {
218
 
        return true;
219
 
    }
220
 
 
221
 
    /**
222
 
     * Do we need to restart the program in order for this change to have
223
 
     * effect?
224
 
     * @return True if a restart is required
225
 
     */
226
 
    public boolean requiresRestart()
227
 
    {
228
 
        return false;
229
 
    }
230
 
 
231
 
    /**
232
 
     * A class to convert between strings and objects of a type
233
 
     */
234
 
    public abstract class TypeConverter
235
 
    {
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);
241
 
    }
242
 
 
243
 
    /**
244
 
     * A class to convert between strings and objects of a type
245
 
     */
246
 
    public class StringTypeConverter extends TypeConverter
247
 
    {
248
 
        public Class getTypeToConvert()
249
 
        {
250
 
            return String.class;
251
 
        }
252
 
 
253
 
        public String getTypeName()
254
 
        {
255
 
            return "text";
256
 
        }
257
 
 
258
 
        public Object getTypeOptions()
259
 
        {
260
 
            return data;
261
 
        }
262
 
 
263
 
        public String toString(Object orig)
264
 
        {
265
 
            return (String) orig;
266
 
        }
267
 
 
268
 
        public Object fromString(String orig)
269
 
        {
270
 
            return orig;
271
 
        }
272
 
    }
273
 
 
274
 
    /**
275
 
     * A class to convert between strings and objects of a type
276
 
     */
277
 
    public class IntegerTypeConverter extends TypeConverter
278
 
    {
279
 
        public Class getTypeToConvert()
280
 
        {
281
 
            return Integer.TYPE;
282
 
        }
283
 
 
284
 
        public String getTypeName()
285
 
        {
286
 
            return "number";
287
 
        }
288
 
 
289
 
        public Object getTypeOptions()
290
 
        {
291
 
            return data;
292
 
        }
293
 
 
294
 
        public String toString(Object orig)
295
 
        {
296
 
            return Convert.int2String(((Integer) orig).intValue());
297
 
        }
298
 
 
299
 
        public Object fromString(String orig)
300
 
        {
301
 
            return new Integer(Convert.string2Int(orig));
302
 
        }
303
 
    }
304
 
 
305
 
    /**
306
 
     * A class to convert between strings and objects of a type
307
 
     */
308
 
    public class StringArrayTypeConverter extends TypeConverter
309
 
    {
310
 
        public Class getTypeToConvert()
311
 
        {
312
 
            return String[].class;
313
 
        }
314
 
 
315
 
        public String getTypeName()
316
 
        {
317
 
            return "array";
318
 
        }
319
 
 
320
 
        public Object getTypeOptions()
321
 
        {
322
 
            return data;
323
 
        }
324
 
 
325
 
        public String toString(Object orig)
326
 
        {
327
 
            return Convert.stringArray2String(((String[]) orig));
328
 
        }
329
 
 
330
 
        public Object fromString(String orig)
331
 
        {
332
 
            return Convert.string2StringArray(orig);
333
 
        }
334
 
    }
335
 
 
336
 
    /**
337
 
     * A class to convert between strings and objects of a type
338
 
     */
339
 
    public class BooleanTypeConverter extends TypeConverter
340
 
    {
341
 
        public Class getTypeToConvert()
342
 
        {
343
 
            return Boolean.TYPE;
344
 
        }
345
 
 
346
 
        public String getTypeName()
347
 
        {
348
 
            return "boolean";
349
 
        }
350
 
 
351
 
        public Object getTypeOptions()
352
 
        {
353
 
            return data;
354
 
        }
355
 
 
356
 
        public String toString(Object orig)
357
 
        {
358
 
            return Convert.boolean2String(((Boolean) orig).booleanValue());
359
 
        }
360
 
 
361
 
        public Object fromString(String orig)
362
 
        {
363
 
            return new Boolean(Convert.string2Boolean(orig));
364
 
        }
365
 
    }
366
 
 
367
 
    /**
368
 
     * A class to convert between strings and objects of a type
369
 
     */
370
 
    public class UserLevelTypeConverter extends TypeConverter
371
 
    {
372
 
        public Class getTypeToConvert()
373
 
        {
374
 
            return UserLevel.class;
375
 
        }
376
 
 
377
 
        public String getTypeName()
378
 
        {
379
 
            return "options";
380
 
        }
381
 
 
382
 
        public Object getTypeOptions()
383
 
        {
384
 
            return UserLevel.getUserLevels();
385
 
        }
386
 
        
387
 
        public String toString(Object orig)
388
 
        {
389
 
            return ((UserLevel) orig).getName();
390
 
        }
391
 
 
392
 
        public Object fromString(String orig)
393
 
        {
394
 
            return UserLevel.forName(orig);
395
 
        }
396
 
    }
397
 
 
398
 
    /**
399
 
     * A class to convert between strings and objects of a type
400
 
     */
401
 
    public class ClassTypeConverter extends TypeConverter
402
 
    {
403
 
        public Class getTypeToConvert()
404
 
        {
405
 
            return Class.class;
406
 
        }
407
 
 
408
 
        public String getTypeName()
409
 
        {
410
 
            return "text";
411
 
        }
412
 
 
413
 
        public Object getTypeOptions()
414
 
        {
415
 
            return data;
416
 
        }
417
 
 
418
 
        public String toString(Object orig)
419
 
        {
420
 
            return ((Class) orig).getName();
421
 
        }
422
 
 
423
 
        public Object fromString(String orig)
424
 
        {
425
 
            try
426
 
            {
427
 
                return Class.forName(orig);
428
 
            }
429
 
            catch (ClassNotFoundException ex)
430
 
            {
431
 
                log.warn("Class not found: "+orig, ex);
432
 
                return null;
433
 
            }
434
 
        }
435
 
    }
436
 
 
437
 
    /**
438
 
     * The converters that we know about.
439
 
     */
440
 
    private TypeConverter[] types = new TypeConverter[]
441
 
    {
442
 
        new StringTypeConverter(),
443
 
        new StringArrayTypeConverter(),
444
 
        new BooleanTypeConverter(),
445
 
        new UserLevelTypeConverter(),
446
 
        new IntegerTypeConverter(),
447
 
        new ClassTypeConverter(),
448
 
    };
449
 
 
450
 
    /**
451
 
     * The method to call to get the value
452
 
     */
453
 
    private Method getter;
454
 
 
455
 
    /**
456
 
     * The method to call to set the value
457
 
     */
458
 
    private Method setter;
459
 
 
460
 
    /**
461
 
     * The class we are going to call a static method on
462
 
     */
463
 
    private Class clazz;
464
 
 
465
 
    /**
466
 
     * The parameter type that we convert to and from
467
 
     */
468
 
    private TypeConverter type;
469
 
 
470
 
    /**
471
 
     * The help text (tooltip) for this item
472
 
     */
473
 
    private String helptext;
474
 
 
475
 
    /**
476
 
     * The userlevel
477
 
     */
478
 
    private UserLevel level;
479
 
 
480
 
    /**
481
 
     * Some options require some extra data in the config file
482
 
     */
483
 
    private String data;
484
 
 
485
 
    /**
486
 
     * The priority of this config level
487
 
     */
488
 
    private int priority;
489
 
 
490
 
    /**
491
 
     * The log stream
492
 
     */
493
 
    protected static Logger log = Logger.getLogger(StaticReflectiveChoice.class);
494
 
}