/*
 * Copyright (c) 2009 The openGion Project.
 *
 * 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 org.opengion.fukurou.system;

import java.util.ResourceBundle;
import java.util.PropertyResourceBundle;
import java.text.MessageFormat;
import java.util.Locale;
import java.util.Arrays;											// 6.4.7.0 (2016/06/03)

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;

/**
 * 共通的に使用されるリソースからメッセージを作成する、ユーティリティークラスです。
 *
 * 現状は、fukurou,hayabusa のフォルダに、それぞれ、messagea.properties ファイルをおきます。
 * これを、読み取る簡易メソッドを用意しておきます。
 *
 * ※ fukurou は、hayabusa の存在を知らないはずですが、簡易メソッドということと、動的作成なので良しとします。
 *
 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
 *
 * @og.group その他
 *
 * @version  6.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK8.0,
 */
public final class MsgUtil {
	/** 初期設定されているリソースバンドルのbaseName {@value} */
	public static final String F_BS_NM = "org.opengion.fukurou.message" ;

	/** 初期設定されているリソースバンドルのbaseName {@value} */
	public static final String H_BS_NM = "org.opengion.hayabusa.message" ;

	/**
	 * "org.opengion.fukurou.message" の、Locale.JAPANESE リソースから取得するメッセージを文字列で返します。
	 *
	 * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した
	 * 文字列を返します。
	 * baseName は、F_BS_NM で、Locale に、Locale.JAPANESE を指定したメッセージを作成します。
	 *
	 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
	 *
	 * @param id	リソースのキーとなるID。
	 * @param args	リソースを、MessageFormat.format で加工する場合の引数。
	 *
	 * @return MessageFormat.formatで加工された文字列
	 * @see		#F_BS_NM
	 */
	public static String getMsg( final String id , final Object... args ) {
		return getMsg( F_BS_NM , Locale.JAPANESE , id , args );
	}

	/**
	 * リソースから取得するメッセージを文字列で返します。
	 * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した
	 * 文字列を返します。
	 *
	 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
	 * @og.rev 6.4.7.0 (2016/06/03) IllegalArgumentException が発生した場合に、見えるようにする。
	 *
	 * @param baseName	バンドルのベース名
	 * @param locale	リソース・バンドルが必要なロケール
	 * @param id	リソースのキーとなるID
	 * @param args	リソースを、MessageFormat.format で加工する場合の引数
	 *
	 * @return MessageFormat.formatで加工された文字列
	 */
	public static String getMsg( final String baseName , final Locale locale , final String id , final Object... args ) {
		// ResourceBundle.getBundle は、キャッシュされる・・・はず。
		final ResourceBundle resource = ResourceBundle.getBundle( baseName , locale , UTF8_CONTROL );		// リソースバンドルのすべてがキャッシュに格納されます。
		// 6.4.7.0 (2016/06/03) IllegalArgumentException が発生した場合に、見えるようにする。
		try {
			return MessageFormat.format( resource.getString( id ) , args );
		}
		catch( IllegalArgumentException ex ) {
			final String errMsg = "MessageFormatエラー:"
						+ " id [" + id + "]"
						+ " Pattern [" + resource.getString( id ) + "]"
						+ " Arguments [" + Arrays.toString( args ) + "]" ;
			throw new OgRuntimeException( errMsg,ex );
		}
	}

	/**
	 * デフォルトコンストラクターをprivateにして、
	 * オブジェクトの生成をさせないようにする。
	 */
	private MsgUtil() {}

	/**
	 * ResourceBundle.Controlは、バンドル・ロード処理中にResourceBundle.getBundleファクトリによって呼び出される一連のコールバック・メソッドを定義します。
	 *
	 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
	 */
	private static final ResourceBundle.Control UTF8_CONTROL = new ResourceBundle.Control() {
		private static final String CHARSET = "UTF-8";

		/**
		 * 指定された形式とロケールを持つ指定されたバンドル名のリソース・バンドルを、指定されたクラス・ローダーを必要に応じて使用してインスタンス化します。
		 *
		 * 指定されたパラメータに対応する使用可能なリソース・バンドルが存在しない場合、このメソッドはnullを返します。
		 * 予想外のエラーが発生したためにリソース・バンドルのインスタンス化が行えない場合には、単純にnullを返す代わりに、
		 * ErrorまたはExceptionをスローすることでエラーを報告する必要があります。 
		 * reloadフラグがtrueの場合、それは、以前にロードされたリソース・バンドルの有効期限が切れたためにこのメソッドが呼び出されたことを示します。 
		 *
		 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
		 *
		 * @param baseName	リソース・バンドルの基底バンドル名。完全指定クラス名
		 * @param locale	リソース・バンドルのインスタンス化対象となるロケール
		 * @param format	ロードされるリソース・バンドルの形式
		 * @param loader	バンドルをロードするために使用するClassLoader
		 * @param reload	バンドルの再ロードを示すフラグ。有効期限の切れたリソース・バンドルを再ロードする場合はtrue、それ以外の場合はfalse
		 *
		 * @return ResourceBundle.Controオブジェクト
		 *
		 * @throws NullPointerException			bundleName、locale、format、またはloaderがnullの場合、またはtoBundleNameからnullが返された場合
		 * @throws IllegalArgumentException		formatが不明である場合、または指定されたパラメータに対して見つかったリソースに不正なデータが含まれている場合。
		 * @throws ClassCastException			ロードされたクラスをResourceBundleにキャストできない場合
		 * @throws IllegalAccessException		クラスまたはその引数なしのコンストラクタにアクセスできない場合。
		 * @throws InstantiationException		クラスのインスタンス化が何かほかの理由で失敗する場合。
		 * @throws ExceptionInInitializerError	このメソッドによる初期化に失敗した場合。
		 * @throws SecurityException			セキュリティ・マネージャが存在し、新しいインスタンスの作成が拒否された場合。詳細は、Class.newInstance()を参照してください。
		 * @throws IOException					何らかの入出力操作を使ってリソースを読み取る際にエラーが発生した場合
		 */
		@Override
		public ResourceBundle newBundle( final String baseName, 
										 final Locale locale, 
										 final String format, 
										 final ClassLoader loader, 
										 final boolean reload ) throws IllegalAccessException, InstantiationException, IOException {
			// The below is a copy of the default implementation.
			final String bundleName   = toBundleName(   baseName  , locale );
			final String resourceName = toResourceName( bundleName, "properties" );
			InputStream stream = null;
			if( reload ) {
				final URL url = loader.getResource( resourceName );
				if( url != null ) {
					final URLConnection connection = url.openConnection();
					if( connection != null ) {
						connection.setUseCaches( false );
						stream = connection.getInputStream();
					}
				}
			} else {
				stream = loader.getResourceAsStream( resourceName );
			}

			ResourceBundle bundle = null;
			if( stream != null ) {
				try {
					// Only this line is changed to make it to read properties files as UTF-8.
					bundle = new PropertyResourceBundle( new BufferedReader( new InputStreamReader( stream,CHARSET ) ) );
				} finally {
					stream.close();
				}
			}
			return bundle;
		}
	};
}
