View Javadoc

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  }