1
20 package org.crosswire.common.config;
21
22 import java.lang.reflect.InvocationTargetException;
23 import java.lang.reflect.Method;
24 import java.util.MissingResourceException;
25 import java.util.ResourceBundle;
26
27 import org.crosswire.common.util.ClassUtil;
28 import org.crosswire.common.util.StringUtil;
29 import org.crosswire.jsword.JSOtherMsg;
30 import org.jdom2.Element;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34
41 public abstract class AbstractReflectedChoice implements Choice {
42
47 public void init(Element option, ResourceBundle configResources) throws StartupException {
48 assert configResources != null;
49
50 key = option.getAttributeValue("key");
51
52 try {
55 String hiddenState = configResources.getString(key + ".hidden");
56 hidden = Boolean.valueOf(hiddenState).booleanValue();
57 } catch (MissingResourceException e) {
58 hidden = false;
59 }
60
61 try {
64 String ignoreState = configResources.getString(key + ".ignore");
65 ignored = Boolean.valueOf(ignoreState).booleanValue();
66 if (ignored) {
67 hidden = true;
68 return;
69 }
70 } catch (MissingResourceException e) {
71 ignored = false;
72 }
73
74 String helpText = configResources.getString(key + ".help");
75 assert helpText != null;
76 setHelpText(helpText);
77
78 String[] pathParts = StringUtil.split(key, '.');
80 StringBuilder parentKey = new StringBuilder();
81 StringBuilder path = new StringBuilder();
82 for (int i = 0; i < pathParts.length; i++) {
83 if (i > 0) {
84 parentKey.append('.');
85 path.append('.');
86 }
87 parentKey.append(pathParts[i]);
88 String parent = configResources.getString(parentKey + ".name");
89 assert parent != null;
90 path.append(parent);
91 }
92 setFullPath(path.toString());
93
94 external = Boolean.valueOf(option.getAttributeValue("external")).booleanValue();
95
96 restart = Boolean.valueOf(option.getAttributeValue("restart")).booleanValue();
97
98 type = option.getAttributeValue("type");
99
100 Element introspector = option.getChild("introspect");
103 if (introspector == null) {
104 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "introspect"));
105 }
106
107 String clazzname = introspector.getAttributeValue("class");
108 if (clazzname == null) {
109 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "class"));
110 }
111
112 propertyname = introspector.getAttributeValue("property");
113 if (propertyname == null) {
114 throw new StartupException(JSOtherMsg.lookupText("Missing {0} element in config.xml", "property"));
115 }
116
117
119 try {
120 clazz = ClassUtil.forName(clazzname);
121 } catch (ClassNotFoundException ex) {
122 throw new StartupException(JSOtherMsg.lookupText("Specified class not found: {0}", clazzname), ex);
123 }
124
125 try {
126 setter = clazz.getMethod("set" + propertyname, getConversionClass());
127 } catch (NoSuchMethodException ex) {
128 throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.set{1}({2} arg0)",
129 clazz.getName(), propertyname, getConversionClass().getName()), ex
130 );
131 }
132
133 try {
134 try {
135 getter = clazz.getMethod("is" + propertyname, new Class[0]);
136 } catch (NoSuchMethodException e) {
137 getter = clazz.getMethod("get" + propertyname, new Class[0]);
138 }
139 } catch (NoSuchMethodException ex) {
140 throw new StartupException(JSOtherMsg.lookupText("Specified method not found {0}.get{1}()", clazz.getName(), propertyname), ex);
141 }
142
143 if (getter.getReturnType() != getConversionClass()) {
144 log.debug("Not using {} from {} because the return type of the getter is not {}", propertyname, clazz.getName(), getConversionClass().getName());
145 throw new StartupException(JSOtherMsg.lookupText("Mismatch of return types, found: {0} required: {1}", getter.getReturnType(), getConversionClass()));
146 }
147 }
148
149
154 public String getKey() {
155 return key;
156 }
157
158
163 public String getType() {
164 return type;
165 }
166
167
173 public abstract String convertToString(Object orig);
174
175
180 public abstract Object convertToObject(String orig);
181
182
187 public String getFullPath() {
188 return fullPath;
189 }
190
191
196 public void setFullPath(String newFullPath) {
197 fullPath = newFullPath;
198 }
199
200
205 public String getHelpText() {
206 return helptext;
207 }
208
209
214 public void setHelpText(String helptext) {
215 this.helptext = helptext;
216 }
217
218
223 public boolean isSaveable() {
224 return !external;
225 }
226
227
232 public boolean isHidden() {
233 return hidden;
234 }
235
236
241 public boolean isIgnored() {
242 return ignored;
243 }
244
245
250 public boolean requiresRestart() {
251 return restart;
252 }
253
254
259 public String getString() {
260 try {
261 Object retval = getter.invoke(null, new Object[0]);
262 return convertToString(retval);
263 } catch (IllegalAccessException ex) {
264 log.error("Illegal access getting value from {}.{}", clazz.getName(), getter.getName(), ex);
265 return "";
266 } catch (InvocationTargetException ex) {
267 log.error("Failed to get value from {}.{}", clazz.getName(), getter.getName(), ex);
268 return "";
269 }
270 }
271
272
277 public void setString(String value) throws ConfigException {
278 Exception ex = null;
279 try {
280 Object object = convertToObject(value);
281 if (object != null) {
282 setter.invoke(null, object);
283 }
284 } catch (InvocationTargetException e) {
285 ex = e;
286 } catch (IllegalArgumentException e) {
287 ex = e;
288 } catch (IllegalAccessException e) {
289 ex = e;
290 } catch (NullPointerException e) {
291 ex = e;
292 }
293
294 if (ex != null) {
295 log.info("Exception while attempting to execute: {}", setter.toString());
296
297 throw new ConfigException(JSOtherMsg.lookupText("Failed to set option: {0}", setter), ex);
301 }
302 }
303
304
307 private String key;
308
309
312 private Class<? extends Object> clazz;
313
314
317 private String propertyname;
318
319
322 private String type;
323
324
327 private Method getter;
328
329
332 private Method setter;
333
334
337 private String helptext;
338
339
342 private String fullPath;
343
344
347 private boolean hidden;
348
349
352 private boolean ignored;
353
354
357 private boolean external;
358
359
362 private boolean restart;
363
364
367 private static final Logger log = LoggerFactory.getLogger(AbstractReflectedChoice.class);
368 }
369