/*
 * Copyright 2013 the original author or authors.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.orangesignal.csv.bean;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * {@link CsvValueConverter} を実装したデフォルトのシンプルな実装クラスを提供します。
 *
 * @author Koji Sugisawa
 */
public class SimpleCsvValueConverter implements CsvValueConverter {

	/**
	 * プリミティブ型とプリミティブ型デフォルト値のマップです。
	 */
	private static final Map<Class<?>, Object> PRIMITIVE_DEFAULTS;

	private static final Map<String, Boolean> BOOLEAN_DEFAULTS;

	static {
		final Map<Class<?>, Object> primitiveMap = new HashMap<Class<?>, Object>();
		primitiveMap.put(Boolean.TYPE, Boolean.FALSE);
		primitiveMap.put(Byte.TYPE, Byte.valueOf((byte) 0));
		primitiveMap.put(Character.TYPE, Character.valueOf('\u0000'));
		primitiveMap.put(Short.TYPE, Short.valueOf((short) 0));
		primitiveMap.put(Integer.TYPE, Integer.valueOf(0));
		primitiveMap.put(Long.TYPE, Long.valueOf(0L));
		primitiveMap.put(Float.TYPE, Float.valueOf(0F));
		primitiveMap.put(Double.TYPE, Double.valueOf(0D));
		PRIMITIVE_DEFAULTS = Collections.unmodifiableMap(primitiveMap);

		final Map<String, Boolean> booleanMap = new HashMap<String, Boolean>();
		putBooleanMap(booleanMap, "0", "1");
		putBooleanMap(booleanMap, "false", "true");
		putBooleanMap(booleanMap, "f", "t");
		putBooleanMap(booleanMap, "no", "yes");
		putBooleanMap(booleanMap, "n", "y");
		putBooleanMap(booleanMap, "off", "on");
		putBooleanMap(booleanMap, "x", "o");
		BOOLEAN_DEFAULTS = Collections.unmodifiableMap(booleanMap);
	}

	private static void putBooleanMap(final Map<String, Boolean> map, final String falseValue, final String trueValue) {
		map.put(falseValue, Boolean.FALSE);
		map.put(trueValue, Boolean.TRUE);
	}

	/**
	 * 日時書式を保持します。
	 */
	private DateFormat dateFormat;

	/**
	 * デフォルトコンストラクタです。
	 */
	public SimpleCsvValueConverter() {
	}

	/**
	 * 日時書式を返します。
	 * 
	 * @return 日時書式
	 */
	public DateFormat getDateFormat() { return dateFormat; }

	/**
	 * 日時書式を設定します。
	 * 
	 * @param dateFormat 日時書式
	 */
	public void setDateFormat(final DateFormat dateFormat) { this.dateFormat = dateFormat; }

	@Override
	public Object convert(final String str, final Class<?> type) {
		// 入力パラメータを検証します。
		if (type == null) {
			throw new IllegalArgumentException("Class must not be null");
		}

		if (str == null) {
			if (type.isPrimitive()) {
				return PRIMITIVE_DEFAULTS.get(type);
			}
			return null;
		}
		if (type.equals(String.class)) {
			return str;
		}

		if (type.equals(Boolean.TYPE) || type.equals(Boolean.class)) {
			for (final Map.Entry<String, Boolean> entry : BOOLEAN_DEFAULTS.entrySet()) {
				if (entry.getKey().equalsIgnoreCase(str)) {
					return entry.getValue();
				}
			}
		} else if (type.equals(Byte.TYPE) || type.equals(Byte.class)) {
			return Byte.valueOf(str);
//		} else if (type.equals(Character.TYPE) || type.equals(Character.class)) {
//			return Character.valueOf(str);
		} else if (type.equals(Short.TYPE) || type.equals(Short.class)) {
			return Short.valueOf(str);
		} else if (type.equals(Integer.TYPE) || type.equals(Integer.class)) {
			return Integer.valueOf(str);
		} else if (type.equals(Long.TYPE) || type.equals(Long.class)) {
			return Long.valueOf(str);
		} else if (type.equals(Float.TYPE) || type.equals(Float.class)) {
			return Float.valueOf(str);
		} else if (type.equals(Double.TYPE) || type.equals(Double.class)) {
			return Double.valueOf(str);
		} else if (type.equals(BigInteger.class)) {
			return new BigInteger(str);
		} else if (type.equals(Number.class) || type.equals(BigDecimal.class)) {
			return new BigDecimal(str);
		} else if (dateFormat != null && Date.class.isAssignableFrom(type)) {
			try {
				return type.getConstructor(Long.TYPE).newInstance(dateFormat.parse(str).getTime());
			} catch (Exception e) {
				throw new IllegalArgumentException(e.getMessage(), e);
			}
		} else if (Enum.class.isAssignableFrom(type)) {
			try {
				return type.getMethod("valueOf", String.class).invoke(null, str);
			} catch (final NoSuchMethodException e) {
				throw new IllegalArgumentException(String.format("Unknown convert type %s", type.getName()), e);
			} catch (final IllegalAccessException e) {
				throw new IllegalArgumentException(String.format("Unknown convert type %s", type.getName()), e);
			} catch (final InvocationTargetException e) {
				throw new IllegalArgumentException(String.format("Unknown convert type %s", type.getName()), e);
			}
		}

		throw new IllegalArgumentException(String.format("Unknown convert type %s", type.getName()));
	}

	@Override
	public String convert(final Object value) {
		if (value == null) {
			return null;
		}
		if (value instanceof Date && dateFormat != null) {
			return dateFormat.format(value);
		}
		return value.toString();
	}

}
@@ -3013,19 +3013,95 @@
 if (
-str == null
+type.equals(String.class)) %7B%0A%09%09%09return str;%0A%09%09%7D%0A%0A%09%09if (str == null %7C%7C str.length() == 0
 ) %7B%0A
@@ -3201,60 +3201,8 @@
 %7D%0A%09%09
-if (type.equals(String.class)) %7B%0A%09%09%09return str;%0A%09%09%7D%0A
 %0A%09%09i
@@ -5486,8 +5486,9 @@
 );%0A%09%7D%0A%0A%7D
+%0A

convert an empty string ("") to null