package org.apache.commons.beanutils;

import java.lang.reflect.Array;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import org.apache.commons.beanutils.converters.BigDecimalConverter;
import org.apache.commons.beanutils.converters.BigIntegerConverter;
import org.apache.commons.beanutils.converters.BooleanArrayConverter;
import org.apache.commons.beanutils.converters.BooleanConverter;
import org.apache.commons.beanutils.converters.ByteArrayConverter;
import org.apache.commons.beanutils.converters.ByteConverter;
import org.apache.commons.beanutils.converters.CharacterArrayConverter;
import org.apache.commons.beanutils.converters.CharacterConverter;
import org.apache.commons.beanutils.converters.ClassConverter;
import org.apache.commons.beanutils.converters.DoubleArrayConverter;
import org.apache.commons.beanutils.converters.DoubleConverter;
import org.apache.commons.beanutils.converters.FloatArrayConverter;
import org.apache.commons.beanutils.converters.FloatConverter;
import org.apache.commons.beanutils.converters.IntegerArrayConverter;
import org.apache.commons.beanutils.converters.IntegerConverter;
import org.apache.commons.beanutils.converters.LongArrayConverter;
import org.apache.commons.beanutils.converters.LongConverter;
import org.apache.commons.beanutils.converters.ShortArrayConverter;
import org.apache.commons.beanutils.converters.ShortConverter;
import org.apache.commons.beanutils.converters.SqlDateConverter;
import org.apache.commons.beanutils.converters.SqlTimeConverter;
import org.apache.commons.beanutils.converters.SqlTimestampConverter;
import org.apache.commons.beanutils.converters.StringArrayConverter;
import org.apache.commons.beanutils.converters.StringConverter;
import org.apache.commons.collections.FastHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class ConvertUtils {
  private static Boolean defaultBoolean = Boolean.FALSE;
  
  public static boolean getDefaultBoolean() {
    return defaultBoolean.booleanValue();
  }
  
  public static void setDefaultBoolean(boolean newDefaultBoolean) {
    defaultBoolean = new Boolean(newDefaultBoolean);
    converters.put(boolean.class, new BooleanConverter(defaultBoolean));
    converters.put(Boolean.class, new BooleanConverter(defaultBoolean));
  }
  
  private static Byte defaultByte = new Byte((byte)0);
  
  public static byte getDefaultByte() {
    return defaultByte.byteValue();
  }
  
  public static void setDefaultByte(byte newDefaultByte) {
    defaultByte = new Byte(newDefaultByte);
    converters.put(byte.class, new ByteConverter(defaultByte));
    converters.put(Byte.class, new ByteConverter(defaultByte));
  }
  
  private static Character defaultCharacter = new Character(' ');
  
  public static char getDefaultCharacter() {
    return defaultCharacter.charValue();
  }
  
  public static void setDefaultCharacter(char newDefaultCharacter) {
    defaultCharacter = new Character(newDefaultCharacter);
    converters.put(char.class, new CharacterConverter(defaultCharacter));
    converters.put(Character.class, new CharacterConverter(defaultCharacter));
  }
  
  private static Double defaultDouble = new Double(0.0D);
  
  public static double getDefaultDouble() {
    return defaultDouble.doubleValue();
  }
  
  public static void setDefaultDouble(double newDefaultDouble) {
    defaultDouble = new Double(newDefaultDouble);
    converters.put(double.class, new DoubleConverter(defaultDouble));
    converters.put(Double.class, new DoubleConverter(defaultDouble));
  }
  
  private static Float defaultFloat = new Float(0.0F);
  
  public static float getDefaultFloat() {
    return defaultFloat.floatValue();
  }
  
  public static void setDefaultFloat(float newDefaultFloat) {
    defaultFloat = new Float(newDefaultFloat);
    converters.put(float.class, new FloatConverter(defaultFloat));
    converters.put(Float.class, new FloatConverter(defaultFloat));
  }
  
  private static Integer defaultInteger = new Integer(0);
  
  public static int getDefaultInteger() {
    return defaultInteger.intValue();
  }
  
  public static void setDefaultInteger(int newDefaultInteger) {
    defaultInteger = new Integer(newDefaultInteger);
    converters.put(int.class, new IntegerConverter(defaultInteger));
    converters.put(Integer.class, new IntegerConverter(defaultInteger));
  }
  
  private static Long defaultLong = new Long(0L);
  
  public static long getDefaultLong() {
    return defaultLong.longValue();
  }
  
  public static void setDefaultLong(long newDefaultLong) {
    defaultLong = new Long(newDefaultLong);
    converters.put(long.class, new LongConverter(defaultLong));
    converters.put(Long.class, new LongConverter(defaultLong));
  }
  
  private static Short defaultShort = new Short((short)0);
  
  public static short getDefaultShort() {
    return defaultShort.shortValue();
  }
  
  public static void setDefaultShort(short newDefaultShort) {
    defaultShort = new Short(newDefaultShort);
    converters.put(short.class, new ShortConverter(defaultShort));
    converters.put(Short.class, new ShortConverter(defaultShort));
  }
  
  private static FastHashMap converters = new FastHashMap();
  
  static {
    converters.setFast(false);
    deregister();
    converters.setFast(true);
  }
  
  private static Log log = LogFactory.getLog(ConvertUtils.class);
  
  public static String convert(Object value) {
    if (value == null)
      return (String)null; 
    if (value.getClass().isArray()) {
      if (Array.getLength(value) < 1)
        return null; 
      value = Array.get(value, 0);
      if (value == null)
        return (String)null; 
      Converter converter1 = (Converter)converters.get(String.class);
      return (String)converter1.convert(String.class, value);
    } 
    Converter converter = (Converter)converters.get(String.class);
    return (String)converter.convert(String.class, value);
  }
  
  public static Object convert(String value, Class clazz) {
    if (log.isDebugEnabled())
      log.debug("Convert string '" + value + "' to class '" + clazz.getName() + "'"); 
    Converter converter = (Converter)converters.get(clazz);
    if (converter == null)
      converter = (Converter)converters.get(String.class); 
    if (log.isTraceEnabled())
      log.trace("  Using converter " + converter); 
    return converter.convert(clazz, value);
  }
  
  public static Object convert(String[] values, Class clazz) {
    Class type = clazz;
    if (clazz.isArray())
      type = clazz.getComponentType(); 
    if (log.isDebugEnabled())
      log.debug("Convert String[" + values.length + "] to class '" + type.getName() + "[]'"); 
    Converter converter = (Converter)converters.get(type);
    if (converter == null)
      converter = (Converter)converters.get(String.class); 
    if (log.isTraceEnabled())
      log.trace("  Using converter " + converter); 
    Object array = Array.newInstance(type, values.length);
    for (int i = 0; i < values.length; i++)
      Array.set(array, i, converter.convert(type, values[i])); 
    return array;
  }
  
  public static void deregister() {
    boolean[] booleanArray = new boolean[0];
    byte[] byteArray = new byte[0];
    char[] charArray = new char[0];
    double[] doubleArray = new double[0];
    float[] floatArray = new float[0];
    int[] intArray = new int[0];
    long[] longArray = new long[0];
    short[] shortArray = new short[0];
    String[] stringArray = new String[0];
    converters.clear();
    converters.put(BigDecimal.class, new BigDecimalConverter());
    converters.put(BigInteger.class, new BigIntegerConverter());
    converters.put(boolean.class, new BooleanConverter(defaultBoolean));
    converters.put(Boolean.class, new BooleanConverter(defaultBoolean));
    converters.put(booleanArray.getClass(), new BooleanArrayConverter(booleanArray));
    converters.put(byte.class, new ByteConverter(defaultByte));
    converters.put(Byte.class, new ByteConverter(defaultByte));
    converters.put(byteArray.getClass(), new ByteArrayConverter(byteArray));
    converters.put(char.class, new CharacterConverter(defaultCharacter));
    converters.put(Character.class, new CharacterConverter(defaultCharacter));
    converters.put(charArray.getClass(), new CharacterArrayConverter(charArray));
    converters.put(Class.class, new ClassConverter());
    converters.put(double.class, new DoubleConverter(defaultDouble));
    converters.put(Double.class, new DoubleConverter(defaultDouble));
    converters.put(doubleArray.getClass(), new DoubleArrayConverter(doubleArray));
    converters.put(float.class, new FloatConverter(defaultFloat));
    converters.put(Float.class, new FloatConverter(defaultFloat));
    converters.put(floatArray.getClass(), new FloatArrayConverter(floatArray));
    converters.put(int.class, new IntegerConverter(defaultInteger));
    converters.put(Integer.class, new IntegerConverter(defaultInteger));
    converters.put(intArray.getClass(), new IntegerArrayConverter(intArray));
    converters.put(long.class, new LongConverter(defaultLong));
    converters.put(Long.class, new LongConverter(defaultLong));
    converters.put(longArray.getClass(), new LongArrayConverter(longArray));
    converters.put(short.class, new ShortConverter(defaultShort));
    converters.put(Short.class, new ShortConverter(defaultShort));
    converters.put(shortArray.getClass(), new ShortArrayConverter(shortArray));
    converters.put(String.class, new StringConverter());
    converters.put(stringArray.getClass(), new StringArrayConverter(stringArray));
    converters.put(Date.class, new SqlDateConverter());
    converters.put(Time.class, new SqlTimeConverter());
    converters.put(Timestamp.class, new SqlTimestampConverter());
  }
  
  public static void deregister(Class clazz) {
    converters.remove(clazz);
  }
  
  public static Converter lookup(Class clazz) {
    return (Converter)converters.get(clazz);
  }
  
  public static void register(Converter converter, Class clazz) {
    converters.put(clazz, converter);
  }
}
