public final class ObjectConverter {

  // Init
  // ---------------------------------------------------------------------------------------

  private static final Map<String, Method> CONVERTERS = new HashMap<String, Method>();

  static {
    // Preload converters.
    Method[] methods = ObjectConverter.class.getDeclaredMethods();
    for (Method method : methods) {
      if (method.getParameterTypes().length == 1) {
        // Converter should accept 1 argument. This skips the convert() method.
        CONVERTERS.put(method.getParameterTypes()[0].getName() + "_" + method.getReturnType().getName(), method);
      }
    }
  }

  private ObjectConverter() {
    // Utility class, hide the constructor.
  }

  // Action
  // -------------------------------------------------------------------------------------

  /**
   * Convert the given object value to the given class.
   * 
   * @param from
   *          The object value to be converted.
   * @param to
   *          The type class which the given object should be converted to.
   * @return The converted object value.
   * @throws NullPointerException
   *           If 'to' is null.
   * @throws UnsupportedOperationException
   *           If no suitable converter can be found.
   * @throws RuntimeException
   *           If conversion failed somehow. This can be caused by at least an
   *           ExceptionInInitializerError, IllegalAccessException or
   *           InvocationTargetException.
   */
  @SuppressWarnings("unchecked")
  public static <T> T convert(Object from, Class<T> to) {

    // Null is just null.
    if (from == null) {
      return null;
    }

    // Can we cast? Then just do it.
    if (to.isAssignableFrom(from.getClass())) {
      return to.cast(from);
    }

    // Lookup the suitable converter.
    String converterId = from.getClass().getName() + "_" + to.getName();
    Method converter = CONVERTERS.get(converterId);
    if (converter == null) {
      throw new UnsupportedOperationException("Cannot convert from " + from.getClass().getName() + " to "
          + to.getName() + ". Requested converter does not exist.");
    }

    // Convert the value.
    try {
      // primitives don't appear to have a .cast() method, which causes a null
      // pointer exception
      if (to.isPrimitive()) {
        return (T) converter.invoke(to, from);
      }
      return to.cast(converter.invoke(to, from));
    } catch (Exception e) {
      throw new RuntimeException("Cannot convert from " + from.getClass().getName() + " to " + to.getName()
          + ". Conversion failed with " + e.getMessage(), e);
    }
  }

  // Converters
  // ---------------------------------------------------------------------------------

  /**
   * Converts Integer to Boolean. If integer value is 0, then return FALSE, else
   * return TRUE.
   * 
   * @param value
   *          The Integer to be converted.
   * @return The converted Boolean value.
   */
  public static Boolean integerToBoolean(Integer value) {
    return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE;
  }

  /**
   * Converts Boolean to Integer. If boolean value is TRUE, then return 1, else
   * return 0.
   * 
   * @param value
   *          The Boolean to be converted.
   * @return The converted Integer value.
   */
  public static Integer booleanToInteger(Boolean value) {
    return value.booleanValue() ? Integer.valueOf(1) : Integer.valueOf(0);
  }

  /**
   * Converts Double to BigDecimal.
   * 
   * @param value
   *          The Double to be converted.
   * @return The converted BigDecimal value.
   */
  public static BigDecimal doubleToBigDecimal(Double value) {
    return new BigDecimal(value.doubleValue());
  }

  /**
   * Converts BigDecimal to Double.
   * 
   * @param value
   *          The BigDecimal to be converted.
   * @return The converted Double value.
   */
  public static Double bigDecimalToDouble(BigDecimal value) {
    return new Double(value.doubleValue());
  }

  /**
   * Converts Integer to String.
   * 
   * @param value
   *          The Integer to be converted.
   * @return The converted String value.
   */
  public static String integerToString(Integer value) {
    return value.toString();
  }

  /**
   * Converts String to Integer.
   * 
   * @param value
   *          The String to be converted.
   * @return The converted Integer value.
   */
  public static Integer stringToInteger(String value) {
    return Integer.valueOf(value);
  }

  /**
   * Converts String to Long.
   * 
   * @param value
   *          The String to be converted.
   * @return The converted Long value.
   */
  public static Long stringToLong(String value) {
    return Long.valueOf(value);
  }

  /**
   * Converts String to long.
   * 
   * @param value
   *          The String to be converted.
   * @return The converted long value.
   */
  public static long stringToLongPrimitive(String value) {
    return stringToLong(value).longValue();
  }

  /**
   * Converts Boolean to String.
   * 
   * @param value
   *          The Boolean to be converted.
   * @return The converted String value.
   */
  public static String booleanToString(Boolean value) {
    return value.toString();
  }

  /**
   * Converts String to Boolean.
   * 
   * @param value
   *          The String to be converted.
   * @return The converted Boolean value.
   */
  public static Boolean stringToBoolean(String value) {
    return Boolean.valueOf(value);
  }

  /**
   * Converts Long to Integer.
   * 
   * @param value
   *          The Long value to be convered.
   * @return Converted Integer value.
   */
  public static Integer longToInteger(Long value) {
    return value.intValue();
  }

  /**
   * Converts Double to Float.
   * 
   * @param value
   *          The Double value to be convered.
   * @return Converted Float value.
   */
  public static Float doubleToFloat(Double value) {
    return value.floatValue();
  }

  /**
   * Converts String to Float.
   * 
   * @param value
   *          The Float value to be convered.
   * @return Converted String value.
   */
  public static Float stringToFloat(String value) {
    return Float.parseFloat(value);

  }

  /**
   * Converts String to Float.
   * 
   * @param value
   *          The Float value to be convered.
   * @return Converted String value.
   */
  public static Double stringToDouble(String value) {
    return Double.parseDouble(value);

  }

  /**
   * Converts Long to primitive.
   * 
   * @param value
   *          The Long value to be convered.
   * @return Converted primitive value.
   */
  public static long longToPrimitive(Long value) {
    if (value == null) {
      return 0L;
    }
    return value.longValue();

  }

  /**
   * Converts Long to String.
   * 
   * @param value
   *          The Long value to be converted.
   * @return Converted String value.
   */
  public static String longToString(Long value) {
    if (value == null) {
      return null;
    }
    return value.toString();
  }

  /**
   * Converts Double to String.
   * 
   * @param value
   *          The Double value to be converted.
   * @return Converted String value.
   */
  public static String longToString(Double value) {
    if (value == null) {
      return null;
    }
    return value.toString();
  }

  /**
   * Converts Date to String.
   * 
   * @param value
   *          The value to be converted.
   * @return Converted value.
   */
  public static String longToString(Date value) {
    if (value == null) {
      return null;
    }

    SimpleDateFormat sdf = new SimpleDateFormat();
    return sdf.format(value);
  }

  /**
   * Converts Integer to primitive.
   * 
   * @param value
   *          The Integer value to be convered.
   * @return Converted primitive value.
   */
  public static int integerToPrimitive(Integer value) {
    if (value == null) {
      return 0;
    }
    return value.intValue();

  }

  /**
   * Converts Long to primitive.
   * 
   * @param value
   *          The Long value to be convered.
   * @return Converted primitive value.
   */
  public static double doubleToPrimitive(Double value) {
    if (value == null) {
      return 0.0;
    }
    return value.doubleValue();

  }

  /**
   * Converts Float to primitive.
   * 
   * @param value
   *          The Float value to be convered.
   * @return Converted primitive value.
   */
  public static double floatToPrimitive(Float value) {
    if (value == null) {
      return 0.0f;
    }
    return value.floatValue();

  }
} public class BooleanParameter extends Parameter {

  public BooleanParameter() {
    super(Boolean.class);
  }

  public BooleanParameter(Class<?> type) {
    super(type);
  }

  public BooleanParameter(String name, Class<?> type) {
    super(name, type);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBinderValue(Service service) {
    String binderValue = getStringValue(service.getBinder());

    return parseStringValue(binderValue);
  }

  private Boolean parseStringValue(String binderValue) {
    Boolean val = null;

    if (binderValue != null) {

      if (binderValue.equals("1")) {
        val = new Boolean(true);
      } else {
        val = new Boolean(binderValue);
      }
    }

    return val;
  }

  /**
   * {@inheritDoc}
   */
  public Object getArgumentValue(Object object, Service service) throws ClassCastException {
    if (object == null) {
      return null;
    }

    if (object.getClass() == Long.class) {
      return (Long) object == 1L;
    }

    if (object.getClass() == String.class) {
      return parseStringValue((String) object);
    }

    return super.getArgumentValue(object, service);
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.INTEGER_VAL;
  }
} public class DateParameter extends Parameter {

  public DateParameter(String name) {
    super(name, Date.class);
  }

  public DateParameter(String name, Class<?> type) {
    super(name, type);
  }

  @Override
  public Object getBinderValue(Service service) {
    String dateString = getStringValue(service.getBinder());

    Date date = null;

    if (dateString != null) {
      date = getFromString(service.getBinder(), dateString);
    }

    return date;
  }

  private Date getFromString(DataBinder binder, String dateString) {
    Date d = null;
    DateFormat dateFormat = new SimpleDateFormat(binder.m_blDateFormat.toSimplePattern());
    try {
      d = (Date) dateFormat.parse(dateString);

    } catch (Exception e) {
      if (dateString != null) {
        throw new IllegalArgumentException("Parameter '" + name + "' must be parseable as a date");
      }
    }

    return d;
  }

  public Object getArgumentValue(Object object, Service service) throws ClassCastException {
    if (object instanceof String) {
      return getFromString(service.getBinder(), (String) object);
    }

    return super.getArgumentValue(object, service);
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.DATE_VAL;
  }
} public class DoubleParameter extends Parameter {

  public DoubleParameter() {
    super("", Double.class);
  }

  public DoubleParameter(String name) {
    super(name, Double.class);
  }

  public DoubleParameter(String name, Class<?> type) {
    super(name, type);
  }

  @Override
  public Object getBinderValue(Service service) {

    String doubleString = getStringValue(service.getBinder());

    Double value = null;

    try {
      value = Double.parseDouble(doubleString);

    } catch (NumberFormatException e) {

      // null is ok (unless param is required) but invalid parse is not.
      if (doubleString != null) {
        throw new IllegalArgumentException("Parameter " + name + " must be parseable as a double");
      }
    }

    return value;
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.FLOAT_VAL;
  }
} public class FloatParameter extends Parameter {

  public FloatParameter() {
    super("", Float.class);
  }

  public FloatParameter(String name) {
    super(name, Float.class);
  }

  public FloatParameter(String name, Class<?> type) {
    super(name, type);
  }

  @Override
  public Object getBinderValue(Service service) {
    
    String floatString = getStringValue(service.getBinder());

    Float value = null;

    try {
      value = Float.parseFloat(floatString);

    } catch (NumberFormatException e) {

      // null is ok (unless param is required) but invalid parse is not.
      if (floatString != null) {
        throw new IllegalArgumentException("Parameter " + name + " must be parseable as a float");
      }
    }

    return value;
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.FLOAT_VAL;
  }
} public class InjectedParameter extends Parameter {

  public InjectedParameter(String name, Class<?> type) {
    super(name, type);
  }

  /**
   * Returns null because the type should be injected rather than retrieved.
   * This is a place-holder not a value retrieval.
   * 
   * @throws IllegalAccessException
   */
  @Override
  public Object getBinderValue(Service service) throws IllegalAccessException {
    return getInjectedValue(service);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getArgumentValue(Object object, Service service) throws ClassCastException {
    return getInjectedValue(service);
  }

  /**
   * Special case for Injectable type. Injects the required parameter from the
   * ExecutionContext (service)
   * 
   * @param service
   * @return
   */
  protected Object getInjectedValue(Service service) {

    if (type == UserData.class) {
      return service.getUserData();

    } else if (type == PageMerger.class) {
      return service.getPageMerger();

    } else if (type == DataBinder.class) {
      return service.getBinder();

    } else if (type == HttpImplementor.class) {
      return service.getHttpImplementor();

    } else if (type == ServiceRequestImplementor.class) {
      return service.getRequestImplementor();

    } else if (type == Workspace.class) {
      return service.getWorkspace();

    } else if (ExecutionContext.class.isAssignableFrom(type)) {
      return service;
    }

    throw new IllegalStateException("Injectable type " + type.getName() + " not known.");
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return -1; // ignore element type in idoc functions
  }

  public static boolean isValidType(Class<?> type) {
    return (type == UserData.class || ExecutionContext.class.isAssignableFrom(type) || type == PageMerger.class
        || type == DataBinder.class || type == HttpImplementor.class || type == ServiceRequestImplementor.class || type == Workspace.class);
  }
} public class IntegerParameter extends Parameter {

  public IntegerParameter(Class<?> type) {
    super(type);
  }

  public IntegerParameter() {
    super(Integer.class);
  }

  public IntegerParameter(String name) {
    super(name, Boolean.class);
  }

  public IntegerParameter(String name, Class<?> type) {
    super(name, type);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBinderValue(Service service) {
    String integerString = getStringValue(service.getBinder());

    return parseStringValue(integerString);
  }

  private Object parseStringValue(String integerString) {
    Integer value = null;

    try {
      value = Integer.parseInt(integerString);

    } catch (NumberFormatException e) {

      // null is ok, but invalid int is not.
      if (integerString != null) {
        throw new IllegalArgumentException("Parameter " + name + " must be parseable as an integer");
      }
    }

    return value;
  }

  @Override
  public Object getArgumentValue(Object object, Service service) throws ClassCastException {
    if (object == null) {
      return null;
    }

    if (object.getClass() == String.class) {
      return parseStringValue((String) object);
    }

    return super.getArgumentValue(object, service);
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.INTEGER_VAL;
  }
} public class LongParameter extends Parameter {

  public LongParameter() {
    super("", Long.class);
  }

  public LongParameter(String name) {
    super(name, Long.class);
  }

  public LongParameter(String name, Class<?> type) {
    super(name, type);
  }

  @Override
  public Object getBinderValue(Service service) {
    String longString = getStringValue(service.getBinder());

    Long value = null;

    try {
      value = Long.parseLong(longString);

    } catch (NumberFormatException e) {

      // null is ok, but invalid int is not.
      if (longString != null) {
        throw new IllegalArgumentException("Parameter " + name + " must be parseable as type Long");
      }
    }

    return value;
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.INTEGER_VAL;
  }
} public class ProviderParameter extends Parameter {

  public ProviderParameter() {
    super("", Provider.class);
  }

  public ProviderParameter(String name) {
    super(name, Provider.class);
  }

  public ProviderParameter(String name, Class<?> type) {
    super(name, type);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBinderValue(Service service) {
    String providerName = getStringValue(service.getBinder());
    Provider provider = Providers.getProvider(providerName);

    if (this.required && provider == null) {
      throw new IllegalArgumentException("Provider '" + providerName + "' could not be found.");
    }

    // providers that are in error will throw an exception if they are required
    if (provider != null && provider.isInError() && this.required) {
      throw new IllegalArgumentException("The provider '" + providerName + "' is not configured correctly!");
    }

    return provider;
  }

  /**
   * Returns a result set found by the passed in string value.
   */
  @Override
  public Object getArgumentValue(Object object, Service service) throws ClassCastException {

    if (object instanceof String) {
      Provider provider = Providers.getProvider((String) object);

      if (provider != null) {
        return provider;
      }
    }
    return null;
    // throw new
    // ClassCastException("Must supply the name of a provider as a string");
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    // string val because the provider name is passed as a string
    return GrammarElement.STRING_VAL;
  }
} public class ResultSetParameter extends Parameter {

  public ResultSetParameter() {
    super("", DataResultSet.class);
  }

  public ResultSetParameter(String name) {
    super(name, DataResultSet.class);
  }

  public ResultSetParameter(String name, Class<?> type) {
    super(name, type);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Object getBinderValue(Service service) {
    DataResultSet drs = getResultSet(this.name, service);

    if (this.required && drs == null) {
      throw new IllegalArgumentException("ResultSet " + this.name + " is required.");
    }

    return drs;
  }

  /**
   * Returns a result set found by the passed in string value.
   */
  @Override
  public Object getArgumentValue(Object object, Service service) throws ClassCastException {

    if (object instanceof String) {
      DataResultSet drs = getResultSet((String) object, service);

      if (drs != null) {
        return drs;
      }
    }
    return null;
    // throw new ClassCastException("Must supply the name of a result set as a string");
  }

  /**
   * Find a result set in the service binder.
   * 
   * @param name
   * @param service
   * @return
   */
  private DataResultSet getResultSet(String name, Service service) {
    ResultSet rs = service.getBinder().getResultSet(name);
    DataResultSet drs = new DataResultSet();

    if (rs != null) {
      drs.copy(rs);

      return drs;
    }

    return null;
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    // string val because the RS name is passed as a string
    return GrammarElement.STRING_VAL;
  }
} public class StringParameter extends Parameter {

  public StringParameter(String name, Class<?> type) {
    super(name, type);
  }

  @Override
  public Object getBinderValue(Service service) {
    return getStringValue(service.getBinder());
  }

  /**
   * {@inheritDoc}
   */
  public int getGrammarElementType() {
    return GrammarElement.STRING_VAL;
  }
}
import intradoc.data.DataBinder;
import intradoc.data.DataResultSet;
import intradoc.data.ResultSet;
import intradoc.provider.Provider;
import intradoc.server.Service;
import java.util.Date;
import net.balusc.util.ObjectConverter;
import org.ucmtwine.parameter.types.BooleanParameter;
import org.ucmtwine.parameter.types.DateParameter;
import org.ucmtwine.parameter.types.DoubleParameter;
import org.ucmtwine.parameter.types.FloatParameter;
import org.ucmtwine.parameter.types.InjectedParameter;
import org.ucmtwine.parameter.types.IntegerParameter;
import org.ucmtwine.parameter.types.LongParameter;
import org.ucmtwine.parameter.types.ProviderParameter;
import org.ucmtwine.parameter.types.ResultSetParameter;
import org.ucmtwine.parameter.types.StringParameter;
package org.ucmtwine.parameter;





/**
 * Encapsulates a binder parameter, taking the legwork out of enforcing type and
 * mandatory requirements.
 * 
 * @author tim
 */
public abstract class Parameter implements IParameter {

  public static final int GRAMMAR_ELEMENT_UNSPECIFIED = -1;

  protected String name;
  protected boolean required = true;

  /**
   * The actual type (may be primitive, non nullable etc)
   */
  protected Class<?> type;

  protected Parameter() {
    this.name = "";
  }

  protected Parameter(Class<?> type) {
    this.type = type;
    this.name = "";
  }

  /**
   * Initialise a basic named binder variable.
   * 
   * @param name
   */
  protected Parameter(String name, Class<?> type) {
    this.name = name;
    this.type = type;
  }

  /**
   * Shortcut to make a named String parameter.
   * 
   * @param name
   * @return
   */
  public static Parameter create(String name) {
    return create(name, String.class, true);
  }

  /**
   * Create an un-named, required, parameter.
   * 
   * @param type
   * @return
   */
  public static Parameter create(Class<?> type) {
    return create("", type, true);
  }

  /**
   * Factory method to create the concrete variable type.
   * 
   * @param name
   *          The name in the binder of the variable.
   * @return
   */
  public static Parameter create(String name, Class<?> type, boolean required) throws IllegalArgumentException {

    if (InjectedParameter.isValidType(type)) {
      return new InjectedParameter(name, type);
    }

    if (type.isPrimitive() && !required) {
      String msg = "Parameter [" + name + "] found with non-nullable type. Use a wrapper type or change to required";
      throw new IllegalArgumentException(msg);
    }

    Parameter p;

    if (type == String.class) {
      p = new StringParameter(name, type);