package jp.co.denso.fa.jmscaosql.util;

import java.io.IOException;
import java.io.InputStream;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Properties;

/**
 * プロパティファイル管理クラス
 * このクラスは、標準でパッケージ内にあるjmscaosql.propertiesファイルを読み込みます。<br>
 * ユーザが指定したい場合は、java.exeが実行されるルートにjmscaosql.propertiesを置きます。<br>
 * デフォルトと同じプロパティキーの場合、ユーザが指定したプロパティが優先されます。
 *
 * @author DENSO WAVE
 * @version 1.00
 */
public class JmsCaoSQLConfigurator {
	/**
	 * 標準で読みこまれるプロパティファイル名
	 */
	static final String DEFAULT_PROPERTIES = "jp/co/denso/fa/jmscaosql/jmscaosql.properties";

	/**
	 * ルートに置かれたユーザプロパティファイル名
	 */
	static final String USER_PROPERTIES = "./jmscaosql.properties";

	/**
	 * キーと値を対にして設定情報を保持します。
	 */
	private static Properties configurations = new Properties();

	/**
	 * このクラスのインスタンス
	 */
	private static JmsCaoSQLConfigurator instance = null;

	/**
	 * <code>ComConfigurator</code> を生成します。
	 */
	private JmsCaoSQLConfigurator() {
		initConfiguration();
	}

	/**
	 * <code>JmsCaoSQLConfigurator</code>インスタンス取得メソッド
	 *
	 * @return JmsCaoSQLConfiguratorのインスタンスを返します。
	 */
	public static synchronized JmsCaoSQLConfigurator getInstance() {
		if (instance == null) {
			instance = new JmsCaoSQLConfigurator();
		}
		return instance;
	}

	/**
	 * 指定されたキーから、 <code>JmsCaoSQLConfigurator</code> が保持する設定情報を 返します。<br>
	 * 指定されたキーが無い場合は、空文字("")を返します。
	 *
	 * @param key キー
	 * @return 設定情報
	 */
	public String getString(String key) {
		String value = configurations.getProperty(key);
		if (value == null) {
			return "";
		} else {
			return value;
		}
	}

	/**
	 * 指定されたキーから、 <code>JmsCaoSQLConfigurator</code> が保持する設定情報を 返します。<br>
	 * 指定されたキーが無い場合は、空のリストを返します。
	 *
	 * @param key キー
	 * @return 設定情報のリスト
	 */
	public List<String> getStringList(String key){
		List<String> retList = new ArrayList<String>();

		String value = configurations.getProperty(key);
		if (value != null && !value.equals("")) {
			retList = Arrays.asList(value.split("," , -1));
		}

		return retList;
	}

	/**
	 * 指定されたキーから、 <code>JmsCaoSQLConfigurator</code> が保持する設定情報を 返します。<br>
	 *
	 * @param key キー
	 * @return 設定情報
	 * @throws {@link NoSuchElementException} 設定ファイル内にkeyが見つからない場合にスローされます。
	 * @throws {@link NumberFormatException} keyで取得できた値をintに変換できない場合にスローされます。
	 */
	public int getInt(String key) throws NoSuchElementException, NumberFormatException {
		String value = configurations.getProperty(key);
		if (value == null) {
			throw new NoSuchElementException(key);
		} else {
			return  Integer.parseInt(value);
		}
	}

	/**
	 * 指定されたキーから、 <code>JmsCaoSQLConfigurator</code> が保持する設定情報を 返します。<br>
	 * 指定されたキーが無い場合は、空リストを返します。
	 * @param key
	 * @return 設定情報(int値)のリスト
	 * @throws {@link NumberFormatException} keyで取得できた値をintに変換できない場合にスローされます。
	 */
	public List<Integer> getIntList(String key) throws NumberFormatException {
		List<Integer> retList = new ArrayList<Integer>();

		for (String str : getStringList(key)) {
			retList.add((Integer.valueOf(str)));
		}

		return retList;
	}

	/**
	 * 指定したプロパティファイルを読み込みます。<br>
	 * すでに取得済みのプロパティ情報があれば、上書きされます。
	 *
	 * @param resource プロパティファイル名
	 */
	private void loadProperties(String resource) {

		ClassLoader classLoader = this.getClass().getClassLoader();

		try {
			InputStream inputStream = getResourceAsStream(classLoader, resource);
			Properties props = new Properties();

			if (inputStream != null) {
				props.load(inputStream);
			}

			configurations.putAll(props);

		} catch (IOException ioe) {
			System.err.println("Cannot get properties. (" + resource + ")");
		}
	}

	/**
	 * <code>JmsCaoSQLConfigurator</code> が持つプロパティ情報を初期化します。
	 */
	private void initConfiguration() {
		configurations.clear();

		loadProperties(DEFAULT_PROPERTIES);
		loadProperties(USER_PROPERTIES);
	}

	/**
	 * 指定されたリソースを {@link InputStream}として取得します。
	 *
	 * @param loader クラスローダ
	 * @param name リソース名
	 * @return 入力ストリーム
	 */
	private InputStream getResourceAsStream(final ClassLoader loader, final String name) {

		return AccessController.doPrivileged(new PrivilegedAction<InputStream>() {
			public InputStream run() {
				if (loader != null) {
					return loader.getResourceAsStream(name);
				} else {
					return ClassLoader.getSystemResourceAsStream(name);
				}
			}
		});
	}
}
