/*
 * RuntimeArgumentsAnalyzer.java
 * Copyright (C) 2008 Cyber Beans Corporation. All rights reserved.
 */
package jp.co.cybec.cb3.session;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import jp.co.cybec.cb3.exception.RuntimeArgumentsAnalyzeException;
import jp.co.cybec.cb3.util.logging.ErrorCodeConstant;
import jp.co.cybec.cb3.util.logging.LogMessageConstant;
import jp.co.cybec.cb3.util.logging.LogMessageFactory;

/**
 * フレームワーク起動時の引数を解析するクラス。<br>
 * 引数を解析し、起動パラメータを取得します。
 * @author Naoko Sugimoto
 */
public class RuntimeArgumentsAnalyzer {

	/**
	 * 起動パラメータ（任意分）のキー。<br>
	 * このキーで指定された引数は、
	 * {@link RuntimeParameters}としてサービスに引き渡されます。
	 */
	public static final String APP_PARAM_KEY = "-AppParams";

	/** 起動パラメータ（任意分） */
	private RuntimeParameters runtimeParameters = null;

	/**
	 * 起動パラメータ（予約分）。<br>
	 * 「-ServiceName」など、フレームワークで使用するパラメータです。
	 */
	private Map<String, String> reservedParameters = new HashMap<String, String>();

	/** メッセージ */
	private static final LogMessageFactory MESSAGE = new LogMessageFactory(
			LogMessageConstant.CB3_MESSAGE_FILENAME);

	/**
	 * コンストラクタ。
	 * @param args フレームワーク起動時の引数
	 * @param parameterKeys 予約パラメータキーの配列
	 */
	public RuntimeArgumentsAnalyzer(String[] args, String... parameterKeys) {
		analizeParameters(args, parameterKeys);
	}

	/**
	 * 起動パラメータ（任意分）を取得します。
	 *
	 * @return 起動パラメータ（任意分）
	 */
	public RuntimeParameters getRuntimeParameters() {
		return runtimeParameters;
	}

	/**
	 * 起動パラメータ（予約分）を取得します。
	 * @param key 起動パラメータのキー
	 * @return 指定されたキーに該当する起動パラメータ（予約分）
	 */
	public String getReservedParameter(String key) {
		return reservedParameters.get(key);
	}

	/**
	 * アプリケーション起動時の引数を解析し、起動パラメータを取得します。<br>
	 * 例）引数：{-ReservedKey1, reservedValue1, -ReservedKey2, reservedValue2, ... , -AppParam, key1=value1, key2=value2}
	 * のとき、解析結果を以下のようにreservedParameters、runtimeParametersに格納します。<br><br>
	 *   reservedParameters（予約分）：Map{-ReservedKey1=reservedValue1, -ReservedKey2=reservedValue2}<br>
	 *   runtimeParameters（任意分）：RuntimeParameters{key1=value1, key2=value2}<br>
	 * @param args アプリケーション起動時の引数
	 * @param reservedKeys 予約キーの配列
	 * TODO パッケージを移動するか、throws節に実行時例外を記述する。
	 */
	private void analizeParameters(String[] args, String... reservedKeys) {

		List<String> reservedKeyList = Arrays.asList(reservedKeys);
		Iterator<String> iterator = Arrays.asList(args).iterator();

		// 起動パラメータ（予約分）を取得。
		while (iterator.hasNext()) {
			// 引数ごとの処理。
			String key = iterator.next();
			// 引数が予約キーだった場合。
			if (reservedKeyList.contains(key)) {
				// 次の引数が存在する場合。
				if (iterator.hasNext()) {
					// 既に取得済みの予約キーである場合、重複して指定されていため例外送出。
					if (reservedParameters.containsKey(key)) {
						throw new RuntimeArgumentsAnalyzeException(MESSAGE.getErrorMessage(
								ErrorCodeConstant.SESSION_001, LogMessageConstant.CB3_SESSION_001,
								key));
					}
					// 次の引数をパラメータ値とみなし、キーと値をセットで格納。
					reservedParameters.put(key, iterator.next());

				// 次の引数が存在しない場合。
				} else {
					// パラメータ値が指定されていないため、例外送出。
					throw new RuntimeArgumentsAnalyzeException(MESSAGE.getErrorMessage(
							ErrorCodeConstant.SESSION_002, LogMessageConstant.CB3_SESSION_002, key));
				}

			// 引数がAppParamキーだった場合。
			} else if (APP_PARAM_KEY.equals(key)) {
				// 以降は起動パラメータ（任意分）とみなす。
				break;

			// 引数がキーではなかった場合
			} else {
				// 例外送出。
				throw new RuntimeArgumentsAnalyzeException(MESSAGE.getErrorMessage(
						ErrorCodeConstant.SESSION_003, LogMessageConstant.CB3_SESSION_003, key));
			}
		}

		// 起動パラメータ（任意分）を取得する。
		analyzeRuntimeParameters(iterator);
	}

	/**
	 * アプリケーション起動引数を解析し、起動パラメータ（任意分）を取得します。
	 * 起動パラメータ（任意分）に渡された「key1=value1 key2=value2 ...」形式の値を"="で分割し、Mapに格納します。
	 * @param iterator アプリケーション起動引数のイテレータ。起動パラメータ（任意分）の開始位置を指す。
	 */
	private void analyzeRuntimeParameters(Iterator<String> iterator) {
		Map<String, String> runtimeParameterMap = new HashMap<String, String>();
		while (iterator.hasNext()) {
			String runtimeParameter = iterator.next();

			// "="の位置を取得する。
			int splitPoint = runtimeParameter.indexOf('=');
			// "="がない場合、例外送出。
			if (splitPoint < 0) {
				throw new RuntimeArgumentsAnalyzeException(MESSAGE.getErrorMessage(
						ErrorCodeConstant.SESSION_004, LogMessageConstant.CB3_SESSION_004,
						runtimeParameter));
			}

			// キーと値を分割してマップに格納する。
			String key = runtimeParameter.substring(0, splitPoint);
			String value = runtimeParameter.substring(splitPoint + 1);
			runtimeParameterMap.put(key, value);
		}
		runtimeParameters = new RuntimeParameters(runtimeParameterMap);
	}
}
