1 package org.apache.velocity.tools.config;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import java.util.Arrays;
23 import java.util.ArrayList;
24 import java.util.List;
25 import java.util.Locale;
26 import org.apache.commons.beanutils.Converter;
27 import org.apache.commons.beanutils.converters.BooleanConverter;
28 import org.apache.commons.beanutils.converters.StringConverter;
29 import org.apache.velocity.tools.ClassUtils;
30 import org.apache.velocity.tools.ConversionUtils;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58 public class Data implements Comparable<Data>
59 {
60 protected static final Type DEFAULT_TYPE = Type.AUTO;
61
62 private String key;
63 private String typeValue;
64 private Object value;
65 private boolean isList;
66 private Class target;
67 private Converter converter;
68
69 public Data()
70 {
71 setType(DEFAULT_TYPE);
72 }
73
74 public void setKey(String key)
75 {
76 this.key = key;
77 }
78
79 public void setValue(Object value)
80 {
81 this.value = value;
82 }
83
84 public void setClassname(String classname)
85 {
86 try
87 {
88 setTargetClass(ClassUtils.getClass(classname));
89 }
90 catch (ClassNotFoundException cnfe)
91 {
92 throw new IllegalArgumentException("Class "+classname+" could not be found.", cnfe);
93 }
94 }
95
96
97
98
99
100 public void setClass(String classname)
101 {
102 setClassname(classname);
103 }
104
105 protected void setType(Type type)
106 {
107 this.isList = type.isList();
108
109
110 if (!type.isCustom())
111 {
112 this.typeValue = type.value();
113 this.target = type.getTarget();
114 this.converter = type.getConverter();
115 }
116 else if (type.isList())
117 {
118
119 this.typeValue = type.value();
120 this.target = type.getTarget();
121 }
122 }
123
124 public void setType(String t)
125 {
126
127 this.typeValue = t;
128
129 Type type = Type.get(this.typeValue);
130 if (type != null)
131 {
132 setType(type);
133 }
134 }
135
136 public void setTargetClass(Class clazz)
137 {
138 this.target = clazz;
139 }
140
141 public void setConverter(Class clazz)
142 {
143 try
144 {
145 convertWith((Converter)clazz.newInstance());
146 }
147 catch (Exception e)
148 {
149 throw new IllegalArgumentException("Class "+clazz+" is not a valid "+Converter.class, e);
150 }
151 }
152
153 public void setConverter(String classname)
154 {
155 try
156 {
157 convertWith((Converter)ClassUtils.getInstance(classname));
158 }
159 catch (Exception e)
160 {
161 throw new IllegalArgumentException("Class "+classname+" is not a valid "+Converter.class, e);
162 }
163 }
164
165
166
167
168
169 public void convertWith(Converter converter)
170 {
171 this.converter = converter;
172 }
173
174 public String getKey()
175 {
176 return this.key;
177 }
178
179 public String getType()
180 {
181 return this.typeValue;
182 }
183
184 public Object getValue()
185 {
186 return this.value;
187 }
188
189 public Class getTargetClass()
190 {
191 return this.target;
192 }
193
194 public Converter getConverter()
195 {
196 return this.converter;
197 }
198
199 public Object getConvertedValue()
200 {
201 return convert(this.value);
202 }
203
204 public void validate()
205 {
206
207 if (getKey() == null)
208 {
209 throw new NullKeyException(this);
210 }
211
212
213 if (getValue() == null)
214 {
215 throw new ConfigurationException(this, "No value has been set for '"+getKey()+'\'');
216 }
217 else if (this.converter != null)
218 {
219 try
220 {
221 if (getConvertedValue() == null && getValue() != null)
222 {
223 throw new ConfigurationException(this, "Conversion of "+getValue()+" for '"+getKey()+"' failed and returned null");
224 }
225 }
226 catch (Throwable t)
227 {
228 throw new ConfigurationException(this, t);
229 }
230 }
231 }
232
233 public int compareTo(Data datum)
234 {
235 if (getKey() == null && datum.getKey() == null)
236 {
237 return 0;
238 }
239 else if (getKey() == null)
240 {
241 return -1;
242 }
243 else if (datum.getKey() == null)
244 {
245 return 1;
246 }
247 else
248 {
249 return getKey().compareTo(datum.getKey());
250 }
251 }
252
253 @Override
254 public int hashCode()
255 {
256 if (getKey() == null)
257 {
258 return super.hashCode();
259 }
260 return getKey().hashCode();
261 }
262
263 @Override
264 public boolean equals(Object obj)
265 {
266 if (getKey() == null || !(obj instanceof Data))
267 {
268 return super.equals(obj);
269 }
270 return getKey().equals(((Data)obj).getKey());
271 }
272
273 @Override
274 public String toString()
275 {
276 StringBuilder out = new StringBuilder();
277 out.append("Data '");
278 out.append(key);
279 out.append('\'');
280 out.append(" -");
281 out.append(this.typeValue);
282 out.append("-> ");
283 out.append(value);
284 return out.toString();
285 }
286
287
288
289 protected Object convert(Object value)
290 {
291 if (this.isList)
292 {
293 return convertList(value);
294 }
295 else if (this.converter == null)
296 {
297 return value;
298 }
299 else
300 {
301 return convertValue(value);
302 }
303 }
304
305 private Object convertValue(Object value)
306 {
307 return this.converter.convert(this.target, value);
308 }
309
310 private List convertList(Object val)
311 {
312
313 String value = (String)val;
314 if (value == null || value.trim().length() == 0)
315 {
316 return null;
317 }
318 else
319 {
320
321 List<String> list = Arrays.asList(value.split(","));
322 if (this.converter == null || this.target.equals(String.class))
323 {
324 return list;
325 }
326 else
327 {
328 List convertedList = new ArrayList();
329 for (String item : list)
330 {
331 convertedList.add(convertValue(item));
332 }
333 return convertedList;
334 }
335 }
336 }
337
338
339
340
341
342
343
344
345
346
347 protected static enum Type
348 {
349 AUTO(Object.class, new AutoConverter()),
350 BOOLEAN(Boolean.class, new BooleanConverter()),
351 CUSTOM(null, null),
352 FIELD(Object.class, new FieldConverter()),
353 NUMBER(Number.class, new NumberConverter()),
354 STRING(String.class, new StringConverter()),
355 LIST(List.class, null),
356 LIST_AUTO(List.class, AUTO.getConverter()),
357 LIST_BOOLEAN(List.class, BOOLEAN.getConverter()),
358 LIST_FIELD(List.class, FIELD.getConverter()),
359 LIST_NUMBER(List.class, NUMBER.getConverter()),
360 LIST_STRING(List.class, STRING.getConverter());
361
362 private Class target;
363 private Converter converter;
364
365 Type(Class t, Converter c)
366 {
367 this.target = t;
368 this.converter = c;
369 }
370
371 public boolean isCustom()
372 {
373
374 return (this.converter == null);
375 }
376
377 public boolean isList()
378 {
379
380 return (this.target == List.class);
381 }
382
383 public Class getTarget()
384 {
385 return this.target;
386 }
387
388 public Converter getConverter()
389 {
390 return this.converter;
391 }
392
393 public String value()
394 {
395
396 return name().replace('_','.').toLowerCase();
397 }
398
399 public static Type get(String type)
400 {
401 if (type == null || type.length() == 0)
402 {
403 return CUSTOM;
404 }
405
406 return valueOf(type.replace('.','_').toUpperCase());
407 }
408 }
409
410 protected static class FieldConverter implements Converter
411 {
412 public Object convert(Class type, Object value)
413 {
414 String fieldpath = (String)value;
415 try
416 {
417 return ClassUtils.getFieldValue(fieldpath);
418 }
419 catch (Exception e)
420 {
421 throw new IllegalArgumentException("Could not retrieve value for field at "+fieldpath, e);
422 }
423 }
424 }
425
426 protected static class AutoConverter implements Converter
427 {
428 public Object convert(Class type, Object obj)
429 {
430
431 if (obj instanceof String)
432 {
433 try
434 {
435 return convert((String)obj);
436
437 }
438 catch (Exception e)
439 {
440 return obj;
441 }
442 }
443 return obj;
444 }
445
446 public Object convert(String value)
447 {
448
449 if (value.matches("true|false|yes|no|y|n|on|off"))
450 {
451 return Type.BOOLEAN.getConverter().convert(Boolean.class, value);
452 }
453
454 else if (value.matches("-?[0-9]+(\\.[0-9]+)?"))
455 {
456 return Type.NUMBER.getConverter().convert(Number.class, value);
457 }
458
459 else if (value.matches("(\\w+\\.)+\\w+"))
460 {
461 return Type.FIELD.getConverter().convert(Object.class, value);
462 }
463 return value;
464 }
465 }
466
467 protected static class NumberConverter implements Converter
468 {
469 public Object convert(Class type, Object obj)
470 {
471 Number num = ConversionUtils.toNumber(obj,"default",Locale.US);
472 if (num == null)
473 {
474 throw new IllegalArgumentException("Could not convert "+obj+" to a number");
475 }
476
477 else if (obj.toString().indexOf('.') < 0)
478 {
479
480 if (num.doubleValue() > Integer.MAX_VALUE ||
481 num.doubleValue() < Integer.MIN_VALUE)
482 {
483 num = Long.valueOf(num.longValue());
484 }
485 else
486 {
487 num = Integer.valueOf(num.intValue());
488 }
489 }
490 return num;
491 }
492 }
493
494 }