/*
 * Copyright 2006 Takahiro Nakamura.
 *
 * 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 woolpack.el.convert;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.DateFormat;
import java.text.NumberFormat;
import java.text.ParseException;

import woolpack.text.BooleanFormat;
import woolpack.text.ToCharacterFormat;
import woolpack.text.ToSQLDateFormat;
import woolpack.text.ToStringFormat;
import woolpack.utils.CheckUtils;
import woolpack.utils.SwitchBuilder;
import woolpack.utils.Switchable;

/**
 * 単純型を変換する{@link Convertable}。
 * 
 * @author nakamura
 * 
 */
public class SimpleConverter implements Convertable {
	private final Switchable<Class, Convertable> parseSwitchable;

	private final NumberFormat numberFormat;

	private final DateFormat dateFormat;

	private final BooleanFormat booleanFormat;

	/**
	 * コンストラクタ。
	 * 
	 * @param numberFormat
	 *            数値フォーマット。
	 * @param dateFormat
	 *            日付フォーマット。
	 * @param booleanFormat
	 *            真偽値フォーマット。
	 * @throws NullPointerException
	 *             引数のいずれかが null の場合。
	 */
	public SimpleConverter(final NumberFormat numberFormat,
			final DateFormat dateFormat, final BooleanFormat booleanFormat) {
		CheckUtils.checkNotNull(numberFormat);

		this.numberFormat = numberFormat;
		this.dateFormat = dateFormat;
		this.booleanFormat = booleanFormat;

		parseSwitchable = new SwitchBuilder<Class, Convertable>()
				.put(String.class, new Convertable() {
					private final Switchable<Class, Convertable>
					formatSwitchable
					= new SwitchBuilder<Class, Convertable>()
							.put(Boolean.class,
									new FormatConverter(booleanFormat)).put(
									java.util.Date.class,
									new FormatConverter(dateFormat)).put(
									Number.class,
									new FormatConverter(numberFormat)).put(
									Byte.class).put(Short.class).put(
									Integer.class).put(Long.class).put(
									Float.class).put(Double.class).put(
									BigInteger.class).put(BigDecimal.class)
							.get(new FormatConverter(new ToStringFormat()));

					public Object convert(final Object beforeValue,
							final Class toType, final String propertyName) {
						return formatSwitchable.get(beforeValue.getClass())
								.convert(beforeValue, toType, propertyName);
					}
				}).put(Boolean.class, new ParseConverter(booleanFormat)).put(
						Character.class,
						new ParseConverter(new ToCharacterFormat()))
				.put(java.sql.Date.class,
						new ParseConverter(new ToSQLDateFormat(dateFormat)))
				.put(java.util.Date.class, new ParseConverter(dateFormat)).put(
						Number.class, new Convertable() {
							public Object convert(final Object beforeValue,
									final Class toType,
									final String propertyName) {
								final Number number;
								if (Number.class.isAssignableFrom(beforeValue
										.getClass())) {
									number = (Number) beforeValue;
								} else {
									try {
										number = numberFormat.parse(beforeValue
												.toString());
									} catch (final ParseException e) {
										throw new IllegalArgumentException(e);
									}
								}
								return AbstractNumberConverter
								.convert(number, toType);
							}
						}).put(Byte.class).put(Short.class).put(Integer.class)
				.put(Long.class).put(Float.class).put(Double.class).put(
						BigInteger.class).put(BigDecimal.class).get(
						ConvertConstants.ECHO);
	}

	public Object convert(final Object beforeValue, final Class toType,
			final String propertyName) {
		return parseSwitchable.get(toType).convert(beforeValue, toType,
				propertyName);
	}

	public BooleanFormat getBooleanFormat() {
		return booleanFormat;
	}

	public DateFormat getDateFormat() {
		return dateFormat;
	}

	public NumberFormat getNumberFormat() {
		return numberFormat;
	}
}
