1 package org.lsst.ccs.command;
2
3 import java.lang.reflect.Constructor;
4 import java.lang.reflect.InvocationTargetException;
5
6 /**
7 * This class is responsible for converting strings to objects of a particular
8 * class. It is used to convert command arguments into objects to be passed to
9 * the corresponding method. The current implementation is very simple, dealing
10 * only with built in types, or types with constructors which take a string. It
11 * would be possible to add back in the cliche functionality which allowed extra
12 * input converters to be registered with the input conversion engine.
13 *
14 * Note that in a distributed system argument conversion is only done on the
15 * remote (server) end, so any special classes used for arguments only need to
16 * be present on the remote (server). In CCS terminology this means that the
17 * classes only need to be present in the subsystem.
18 *
19 * @author tonyj
20 */
21 public class InputConversionEngine {
22
23 /**
24 * Convert the input string to an object of the given class
25 * @param arg The string to be converted
26 * @param type The type that the string should be converted to
27 * @return The converted argument
28 * @throws CommandInvocationException If the string cannot be converted to the requested type
29 */
30 public Object convertArgToType(String arg, Class type) throws CommandInvocationException {
31 try {
32 if (type.equals(String.class) || type.isInstance(arg)) {
33 return arg;
34 } else if (type.equals(Integer.class) || type.equals(Integer.TYPE)) {
35 // Want to handle 0x800000000, e.g., which is an invalid int
36 long value = Long.decode(arg);
37 long sign = value >> 32;
38 if (sign != 0 && sign != -1) {
39 throw new NumberFormatException();
40 }
41 return (int)value;
42 } else if (type.equals(Long.class) || type.equals(Long.TYPE)) {
43 return Long.decode(arg);
44 } else if (type.equals(Double.class) || type.equals(Double.TYPE)) {
45 return Double.valueOf(arg);
46 } else if (type.equals(Float.class) || type.equals(Float.TYPE)) {
47 return Float.valueOf(arg);
48 } else if (type.equals(Boolean.class) || type.equals(Boolean.TYPE)) {
49 // Boolean.parseBoolean treats anything it does not recognize as true, which does not
50 // seem appropriate here, so instead
51 if ("true".equalsIgnoreCase(arg)) return Boolean.TRUE;
52 if ("false".equalsIgnoreCase(arg)) return Boolean.FALSE;
53 throw new CommandInvocationException("Error: Can't convert string '%s' to Boolean", arg);
54 } else if (type.isEnum()) {
55 //FIXME: This is case sensitive, is this what we want?
56 //FIXME: Would be nice to generate exception with list of legal values?
57 return Enum.valueOf(type, arg.toUpperCase());
58 } else {
59 Constructor c = type.getConstructor(String.class);
60 return c.newInstance(arg);
61
62 }
63 } catch (InstantiationException | IllegalAccessException | InvocationTargetException ex) {
64 throw new CommandInvocationException("Error: Can't instantiate class %s using string '%s'", ex, type.getName(), arg);
65 } catch (IllegalArgumentException | NoSuchMethodException e) {
66 throw new CommandInvocationException("Error: Can't convert string '%s' to class %s", e, arg, type.getName());
67 }
68 }
69 }