1
22 package org.crosswire.common.config;
23
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.util.MissingResourceException;
27 import java.util.ResourceBundle;
28
29 import org.crosswire.common.util.ClassUtil;
30 import org.crosswire.common.util.Logger;
31 import org.crosswire.common.util.StringUtil;
32 import org.crosswire.jsword.JSOtherMsg;
33 import org.jdom.Element;
34
35
43 public abstract class AbstractReflectedChoice implements Choice {
44
49 public void init(Element option, ResourceBundle configResources) throws StartupException {
50 assert configResources != null;
51
52 key = option.getAttributeValue("key");
53
54 try {
57 String hiddenState = configResources.getString(key + ".hidden");
58 hidden = Boolean.valueOf(hiddenState).booleanValue();
59 } catch (MissingResourceException e) {
60 hidden = false;
61 }
62
63 try {
66 String ignoreState = configResources.getString(key + ".ignore");
67 ignored = Boolean.valueOf(ignoreState).booleanValue();
68 if (ignored) {
69 hidden = true;
70 return;
71 }
72 } catch (MissingResourceException e) {
73 ignored = false;
74 }
75
76 String helpText = configResources.getString(key + ".help");
77 assert helpText != null;
78 setHelpText(helpText);
79
80 String[] pathParts = StringUtil.split(key, '.');
82 StringBuilder parentKey = new StringBuilder();
83 StringBuilder path = new StringBuilder();
84 for (int i = 0; i < pathParts.length; i++) {
85 if (i > 0) {
86 parentKey.append('.');
87 path.append('.');
88 }
89 parentKey.append(pathParts[i]);
90 String parent = configResources.getString(parentKey + ".name");
91 assert parent != null;
92 path.append(parent);
93 }
94 setFullPath(path.toString());
95
96 external = Boolean.valueOf(option.getAttributeValue("external")).booleanValue();
97
98 restart = Boolean.valueOf(option.getAttributeValue("restart")).booleanValue();
99
100 type = option.getAttributeValue("type");
101
102 Element introspector = option.getChild("introspect");
105 if (introspector == null) {
106 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "introspect"));
107 }
108
109 String clazzname = introspector.getAttributeValue("class");
110 if (clazzname == null) {
111 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "class"));
112 }
113
114 propertyname = introspector.getAttributeValue("property");
115 if (propertyname == null) {
116 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "property"));
117 }
118
119
123 try {
124 clazz = ClassUtil.forName(clazzname);
125 } catch (ClassNotFoundException ex) {
126 throw new StartupException(JSOtherMsg.lookupText("Specified class not found: {0}", clazzname), ex);
127 }
128
129 try {
130 setter = clazz.getMethod("set" + propertyname, getConversionClass());
131 } catch (NoSuchMethodException ex) {
132 throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.set{1}({2} arg0)",
133 clazz.getName(), propertyname, getConversionClass().getName()), ex
134 );
135 }
136
137 try {
138 try {
139 getter = clazz.getMethod("is" + propertyname, new Class[0]);
140 } catch (NoSuchMethodException e) {
141 getter = clazz.getMethod("get" + propertyname, new Class[0]);
142 }
143 } catch (NoSuchMethodException ex) {
144 throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.get{1}()", clazz.getName(), propertyname), ex);
145 }
146
147 if (getter.getReturnType() != getConversionClass()) {
148 log.debug("Not using " + propertyname + " from " + clazz.getName() + " because the return type of the getter is not " + getConversionClass().getName());
149 throw new StartupException(JSOtherMsg.lookupText("Mismatch of return types, found: {0} required: {1}", getter.getReturnType(), getConversionClass()));
150 }
151 }
152
153
158 public String getKey() {
159 return key;
160 }
161
162
167 public String getType() {
168 return type;
169 }
170
171
174 public abstract String convertToString(Object orig);
175
176
179 public abstract Object convertToObject(String orig);
180
181
186 public String getFullPath() {
187 return fullPath;
188 }
189
190
195 public void setFullPath(String newFullPath) {
196 fullPath = newFullPath;
197 }
198
199
204 public String getHelpText() {
205 return helptext;
206 }
207
208
213 public void setHelpText(String helptext) {
214 this.helptext = helptext;
215 }
216
217
222 public boolean isSaveable() {
223 return !external;
224 }
225
226
231 public boolean isHidden() {
232 return hidden;
233 }
234
235
240 public boolean isIgnored() {
241 return ignored;
242 }
243
244
249 public boolean requiresRestart() {
250 return restart;
251 }
252
253
258 public String getString() {
259 try {
260 Object retval = getter.invoke(null, new Object[0]);
261 return convertToString(retval);
262 } catch (IllegalAccessException ex) {
263 log.error("Illegal access getting value from " + clazz.getName() + "." + getter.getName(), ex);
264 return "";
265 } catch (InvocationTargetException ex) {
266 log.error("Failed to get value from " + clazz.getName() + "." + getter.getName(), ex);
267 return "";
268 }
269 }
270
271
276 public void setString(String value) throws ConfigException {
277 Exception ex = null;
278 try {
279 Object object = convertToObject(value);
280 if (object != null) {
281 setter.invoke(null, object);
282 }
283 } catch (InvocationTargetException e) {
284 ex = e;
285 } catch (IllegalArgumentException e) {
286 ex = e;
287 } catch (IllegalAccessException e) {
288 ex = e;
289 } catch (NullPointerException e) {
290 ex = e;
291 }
292
293 if (ex != null) {
294 log.info("Exception while attempting to execute: " + setter.toString());
295
296 throw new ConfigException(JSOtherMsg.lookupText("Failed to set option: {0}", setter), ex);
300 }
301 }
302
303
306 private String key;
307
308
311 private Class<? extends Object> clazz;
312
313
316 private String propertyname;
317
318
321 private String type;
322
323
326 private Method getter;
327
328
331 private Method setter;
332
333
336 private String helptext;
337
338
341 private String fullPath;
342
343
346 private boolean hidden;
347
348
351 private boolean ignored;
352
353
356 private boolean external;
357
358
361 private boolean restart;
362
363
366 private static final Logger log = Logger.getLogger(AbstractReflectedChoice.class);
367 }
368