#% CSVファイル入力 Javaソースファイル作成用の雛型ファイル
#% 2009/05/07 By S.Ito

#! package
package #package#;

#! csv in 1
#loggerComment#import org.apache.log4j.Logger;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.LinkedList;
import java.util.List;

/**
 * #nameJp# CSVファイル入力
 *
 * @author
 *
 */
public class #mstName#CsvFileIn implements RecordSearch {

	// 入力ファイルのキャラクタセット
	protected String characterSetName = "JISAutoDetect";

	// 入力ファイル名
	protected String inputFileNname;

	// ヘッダ入力フラグ
	protected boolean inputHeader;

	// 入力件数（正常件数）
	protected int inputCount;

	// 入力エラー件数
	protected int errorCount;

	// 日付フォーマーット
	protected static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd");

	// 時間フォーマーット
	protected static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");

	// 日時フォーマーット
	protected static final SimpleDateFormat dateTimeFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");

	// ロガー
	#loggerComment#private static Logger logger = Logger.getLogger(#mstName#CsvFileIn.class);

	/**
	 * コンストラクタ
	 *
	 * @param inputFileNname
	 *            CSV入力ファイル名
	 * @param inputHeader
	 *            ヘッダ入力フラグ true=CSV入力ファイルにはヘッダ有り
	 */
	public #mstName#CsvFileIn(String inputFileNname, boolean inputHeader) {
		this.inputFileNname = inputFileNname;
		this.inputHeader = inputHeader;
		dateFormat.setLenient(false);
		timeFormat.setLenient(false);
		dateTimeFormat.setLenient(false);
	}

	/**
	 * CSVフィル入力
	 * 
	 * @param recordAction
	 *            レコード毎の処理オブジェクト
	 * @throws Exception
	 *             エラー発生時
	 */
	public void csvFileIn(RecordAction recordAction) throws Exception {

		try {
			#loggerComment#logger.info("START #nameJp#・CSVファイル入力処理");

			// 処理開始
			search(recordAction);

			#loggerComment#logger.info("END   #nameJp#・CSVファイル入力処理 入力件数 = " + getInputCount() + ", エラー行数 = " + getErrorCount());

		} catch (Exception e) {
			#loggerComment#logger.fatal("#nameJp#・CSVファイル入力処理でエラーが発生しました", e);
			throw e;
		}
	}

	/**
	 * 入力行数を取得する
	 *
	 * @return 正常に入力できた行数
	 */
	public int getInputCount() {
		return inputCount;
	}

	/**
	 * エラー行数を取得する
	 *
	 * @return エラー行数
	 */
	public int getErrorCount() {
		return errorCount;
	}

	@Override
	public void search(RecordAction action) throws Exception {
		int lineCount = 0;
		inputCount = 0;
		errorCount = 0;
		BufferedReader br = null;

		try {
			action.initAction();

			try {

				// 入力ファイルをオープン
				br = new BufferedReader(new InputStreamReader(new FileInputStream(inputFileNname), characterSetName));

				// ヘッダスキップ
				if (inputHeader) {
					br.readLine();
				}

				while (true) {
					String line = br.readLine();
					if (line == null) {
						break;
					}
					lineCount ++;

					Object record = null;
					try {
						record = getRecord(line);
						inputCount++;
					} catch (RuntimeException e) {
						errorCount++;
						#loggerComment#logger.error(lineCount + "行目にエラーデータが存在します この行は読み飛ばします " + e.getMessage());
					}

					if (record != null) {
						action.action(record);
					}
				}

			} finally {
				if (br != null) {
					br.close();
					br = null;
				}
			}
		} finally {
			action.closeAction();

		}
	}

	/**
	 * CSVの1行から#mstName#オブジェクトを取得する
	 *
	 * @param line
	 *            CSVファイルの1行
	 * @return #mstName#オブジェクト
	 * @throws RuntimeException
	 */
	protected #mstName# getRecord(String line) throws RuntimeException {

		String[] fieldData = csvLineSplit(line);

		// 1レコード組み立て
		if (fieldData.length != #fieldLength#) {
			throw new RuntimeException("フィールド数が違います");
		}

		int index = 0;
		#mstName# rec = new #mstName#();
#!

#! cav in string
		rec.#setMethod#(checkAndChangeString(fieldData[index++], #length#));
#! cav in int
		rec.#setMethod#(checkAndChangeInt(fieldData[index++]));
#! cav in long
		rec.#setMethod#(checkAndChangeLong(fieldData[index++]));
#! cav in bigdecimal
		rec.#setMethod#(checkAndChangeBigDecimal(fieldData[index++]));
#! cav in date
		rec.#setMethod#(checkAndChangeDate(fieldData[index++]));
#! cav in time
		rec.#setMethod#(checkAndChangeTime(fieldData[index++]));
#! cav in timestamp
		rec.#setMethod#(checkAndChangeTimestamp(fieldData[index++]));
#! cav in bytes
		rec.#setMethod#(checkAndChangeBytes(fieldData[index++], #length#L));
#! cav in boolean
		rec.#setMethod#(checkAndChangeBoolean(fieldData[index++]));
#!

#! csv in 2
		return rec;
	}

	/**
	 * CSVファイルの1行をフィールド毎に分解する
	 *
	 * @param line
	 *            1行
	 * @return 分解された文字列
	 */
	protected static String[] csvLineSplit(String line) {

		List<String> list = new LinkedList<String>();
		StringBuilder sb = new StringBuilder();
		int mode = 0;

		for (int i = 0; i < line.length(); i++) {
			char c = line.charAt(i);
			if (mode == 0) {
				if (c == '\"') {
					mode = 1;
				} else if (c == ',') {
					list.add("");
				} else if (c != ' ' && c != '\t') {
					mode = 2;
					sb.append(c);
				}
			} else if (mode == 1) { // ダブルコーテーション始まりでのデータ取り込み
				if (c != '\"') {
					sb.append(c);
				} else {
					if (i < (line.length() - 1) && line.charAt(i + 1) == '\"') {
						sb.append(c);
						i++;
					} else {
						list.add(sb.toString().trim());
						sb.setLength(0);
						mode = 3;
					}
				}
			} else if (mode == 2) { // ダブルコーテーション無しでのデータ取り込み
				if (c != ',') {
					sb.append(c);
				} else {
					list.add(sb.toString().trim());
					sb.setLength(0);
					mode = 0;
				}
			} else { // カンマまでスキップ
				if (c == ',') {
					mode = 0;
				}
			}

		}

		if (mode != 3) {
			list.add(sb.toString().trim());
		}

		return list.toArray(new String[0]);
	}

	/**
	 * 文字列のチェック＆変換を行う
	 *
	 * @param value
	 * @param maxLength
	 * @return 文字列
	 */
	protected String checkAndChangeString(String value, int maxLength) {
		if (value.length() > maxLength) {
			return value.substring(0, maxLength);
		}

		return value;
	}

	/**
	 * 文字列をInteger型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Integerオブジェクト
	 * @throws ParseException
	 */
	protected Integer checkAndChangeInt(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return Integer.parseInt(value);
		} catch (NumberFormatException e) {
			throw new RuntimeException("データ変換エラー(Integer)", e);
		}
	}

	/**
	 * 文字列をLong型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Longオブジェクト
	 * @throws ParseException
	 */
	protected Long checkAndChangeLong(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return Long.parseLong(value);
		} catch (NumberFormatException e) {
			throw new RuntimeException("データ変換エラー(Long)", e);
		}
	}

	/**
	 * 文字列をBigDecimal型のオブジェクトに変更する
	 *
	 * @param value
	 * @return BigDecimalオブジェクト
	 * @throws ParseException
	 */
	protected BigDecimal checkAndChangeBigDecimal(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return new BigDecimal(value);
		} catch (NumberFormatException e) {
			throw new RuntimeException("データ変換エラー(BigDecimal)", e);
		}
	}

	/**
	 * 文字列をDate型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Dateオブジェクト
	 * @throws ParseException
	 */
	protected Date checkAndChangeDate(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return new Date(dateFormat.parse(value).getTime());
		} catch (ParseException e) {
			throw new RuntimeException("データ変換エラー(Date)", e);
		}
	}

	/**
	 * 文字列をTime型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Timeオブジェクト
	 * @throws ParseException
	 */
	protected Time checkAndChangeTime(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return new Time(timeFormat.parse(value).getTime());
		} catch (ParseException e) {
			throw new RuntimeException("データ変換エラー(Time)", e);
		}
	}

	/**
	 * 文字列をTimestamp型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Timestampオブジェクト
	 * @throws ParseException
	 */
	protected Timestamp checkAndChangeTimestamp(String value) {
		if (value.length() <= 0) {
			return null;
		}

		try {
			return new Timestamp(dateTimeFormat.parse(value).getTime());
		} catch (ParseException e) {
			throw new RuntimeException("データ変換エラー(Timestamp)", e);
		}
	}

	/**
	 * 文字列をBoolean型のオブジェクトに変更する
	 *
	 * @param value
	 * @return Booleanオブジェクト
	 * @throws ParseException
	 */
	protected Boolean checkAndChangeBoolean(String value) {
		if (value.length() <= 0) {
			return null;
		}

		return Boolean.parseBoolean(value);
	}

	/**
	 * 文字列をbyte[]型のオブジェクトに変更する
	 *
	 * @param value
	 * @return byte[]オブジェクト
	 * @throws ParseException
	 */
	protected byte[] checkAndChangeBytes(String value, long maxLength) {
		if (value.length() <= 0) {
			return null;
		}
		ByteArrayOutputStream os = null;
		try {
			os = new ByteArrayOutputStream();

			long length = 0;
			for (int index = 0; (index < value.length() - 1) && length < maxLength; index += 2, length++) {
				os.write((byte) Integer.parseInt(value.substring(index, index + 2), 16));
			}
		} catch (NumberFormatException e) {
			throw new RuntimeException("データ変換エラー(byte[])", e);
		} finally {
			if (os != null) {
				try {
					os.close();
				} catch (IOException e) {
					// 何もしない
				}
			}
		}

		return os.toByteArray();
	}
#!

#! csv in main

	/**
	 * メインルーチン
	 *
	 * @param args 引数 1番目に出力を行うCSVファイル名
	 */
	public static void main(String[] args) {

		// パラメータチェック
		if (args.length < 1) {
			System.out.println("パラメーターエラー");
			System.exit(1);
		}
		
		DaoTransaction tran = null;
		try {
			try {
				// トランザクション作成
				tran = new DaoTransaction();

				// DAO作成
				#mstName#Dao dao = new #mstName#Dao(tran);

				// 自クラスのオブジェクト作成
				#mstName#CsvFileIn myObj = new #mstName#CsvFileIn(args[0], true);

				// レコード毎に処理を行うアクションクラス作成
				InsertAndUpdatDbRecordAction action = myObj.new InsertAndUpdatDbRecordAction(dao);

				// 処理開始
				myObj.csvFileIn(action);

				// トランザクションコミット
				tran.commit();

				// 件数表示
				System.out.println("入力件数　:" + myObj.getInputCount());
				System.out.println("エラー件数:" + myObj.getErrorCount());
				System.out.println("挿入件数　:" + action.getInsertCount());
				System.out.println("更新件数　:" + action.getUpdateCount());
				System.exit(0);

			} finally {
				tran.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(1);
		}
	}

	/**
	 * データ挿入・更新レコードアクションクラス
	 */
	public class InsertAndUpdatDbRecordAction implements RecordAction {

		private int insertCount = 0;
		private int updateCount = 0;
		private #mstName#Dao dao;
		
		public InsertAndUpdatDbRecordAction(#mstName#Dao dao) {
			this.dao = dao;
		}

		@Override
		public void action(Object record) throws Exception {
			if (dao.update((#mstName#) record) > 0) {
				updateCount++;
			} else {
				insertCount += dao.insert((#mstName#) record);
			}
		}

		@Override
		public void closeAction() throws Exception {
		}

		@Override
		public void initAction() throws Exception {
		}

		public int getInsertCount() {
			return insertCount;
		}

		public int getUpdateCount() {
			return updateCount;
		}
	}
#! csv in 3
}
