001/*
002 * Copyright (c) 2009 The openGion Project.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
013 * either express or implied. See the License for the specific language
014 * governing permissions and limitations under the License.
015 */
016package org.opengion.fukurou.system;
017
018import java.util.ResourceBundle;
019import java.util.PropertyResourceBundle;
020import java.util.Locale;
021import java.util.Arrays;                                                                                        // 6.4.7.0 (2016/06/03)
022import java.text.MessageFormat;
023
024import java.io.InputStream;
025import java.io.InputStreamReader;
026import java.io.BufferedReader;
027import java.io.IOException;
028import java.net.URL;
029import java.net.URLConnection;
030
031/**
032 * 共通的に使用されるリソースからメッセージを作成する、ユーティリティークラスです。
033 *
034 * 現状は、fukurou,hayabusa のフォルダに、それぞれ、messagea.properties ファイルをおきます。
035 * これを、読み取る簡易メソッドを用意しておきます。
036 *
037 * ※ fukurou は、hayabusa の存在を知らないはずですが、簡易メソッドということと、動的作成なので良しとします。
038 *
039 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
040 *
041 * @og.group その他
042 *
043 * @version  6.0
044 * @author       Kazuhiko Hasegawa
045 * @since    JDK8.0,
046 */
047public final class MsgUtil {
048        /** 初期設定されているリソースバンドルのbaseName {@value} */
049        public static final String F_BS_NM = "org.opengion.fukurou.message" ;
050
051        /** 初期設定されているリソースバンドルのbaseName {@value} */
052        public static final String H_BS_NM = "org.opengion.hayabusa.message" ;
053
054        /**
055         * "org.opengion.fukurou.message" の、Locale.JAPANESE リソースから取得するメッセージを文字列で返します。
056         *
057         * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した
058         * 文字列を返します。
059         * baseName は、F_BS_NM で、Locale に、Locale.JAPANESE を指定したメッセージを作成します。
060         *
061         * @og.rev 6.4.3.1 (2016/02/12) 新規追加
062         *
063         * @param id    リソースのキーとなるID。
064         * @param args  リソースを、MessageFormat.format で加工する場合の引数。
065         *
066         * @return MessageFormat.formatで加工された文字列
067         * @see         #F_BS_NM
068         */
069        public static String getMsg( final String id , final Object... args ) {
070                return getMsg( F_BS_NM , Locale.JAPANESE , id , args );
071        }
072
073        /**
074         * リソースから取得するメッセージを文字列で返します。
075         * id と引数を受け取り、ResourceBundle と、MessageFormat.format で加工した
076         * 文字列を返します。
077         *
078         * @og.rev 6.4.3.1 (2016/02/12) 新規追加
079         * @og.rev 6.4.7.0 (2016/06/03) IllegalArgumentException が発生した場合に、見えるようにする。
080         *
081         * @param baseName      バンドルのベース名
082         * @param locale        リソース・バンドルが必要なロケール
083         * @param id    リソースのキーとなるID
084         * @param args  リソースを、MessageFormat.format で加工する場合の引数
085         *
086         * @return MessageFormat.formatで加工された文字列
087         */
088        public static String getMsg( final String baseName , final Locale locale , final String id , final Object... args ) {
089                // ResourceBundle.getBundle は、キャッシュされる・・・はず。
090                final ResourceBundle resource = ResourceBundle.getBundle( baseName , locale , UTF8_CONTROL );           // リソースバンドルのすべてがキャッシュに格納されます。
091                // 6.4.7.0 (2016/06/03) IllegalArgumentException が発生した場合に、見えるようにする。
092                try {
093                        return MessageFormat.format( resource.getString( id ) , args );
094                }
095                catch( final IllegalArgumentException ex ) {
096                        final String errMsg = "MessageFormatエラー:"
097                                                + " id [" + id + "]"
098                                                + " Pattern [" + resource.getString( id ) + "]"
099                                                + " Arguments [" + Arrays.toString( args ) + "]" ;
100                        throw new OgRuntimeException( errMsg,ex );
101                }
102        }
103
104        /**
105         * デフォルトコンストラクターをprivateにして、
106         * オブジェクトの生成をさせないようにする。
107         */
108        private MsgUtil() {}
109
110        /**
111         * ResourceBundle.Controlは、バンドル・ロード処理中にResourceBundle.getBundleファクトリによって呼び出される一連のコールバック・メソッドを定義します。
112         *
113         * @og.rev 6.4.3.1 (2016/02/12) 新規追加
114         */
115        private static final ResourceBundle.Control UTF8_CONTROL = new ResourceBundle.Control() {
116                private static final String CHARSET = "UTF-8";
117
118                /**
119                 * 指定された形式とロケールを持つ指定されたバンドル名のリソース・バンドルを、指定されたクラス・ローダーを必要に応じて使用してインスタンス化します。
120                 *
121                 * 指定されたパラメータに対応する使用可能なリソース・バンドルが存在しない場合、このメソッドはnullを返します。
122                 * 予想外のエラーが発生したためにリソース・バンドルのインスタンス化が行えない場合には、単純にnullを返す代わりに、
123                 * ErrorまたはExceptionをスローすることでエラーを報告する必要があります。
124                 * reloadフラグがtrueの場合、それは、以前にロードされたリソース・バンドルの有効期限が切れたためにこのメソッドが呼び出されたことを示します。
125                 *
126                 * @og.rev 6.4.3.1 (2016/02/12) 新規追加
127                 *
128                 * @param baseName      リソース・バンドルの基底バンドル名。完全指定クラス名
129                 * @param locale        リソース・バンドルのインスタンス化対象となるロケール
130                 * @param format        ロードされるリソース・バンドルの形式
131                 * @param loader        バンドルをロードするために使用するClassLoader
132                 * @param reload        バンドルの再ロードを示すフラグ。有効期限の切れたリソース・バンドルを再ロードする場合はtrue、それ以外の場合はfalse
133                 *
134                 * @return ResourceBundle.Controオブジェクト
135                 *
136                 * @throws NullPointerException                 bundleName、locale、format、またはloaderがnullの場合、またはtoBundleNameからnullが返された場合
137                 * @throws IllegalArgumentException             formatが不明である場合、または指定されたパラメータに対して見つかったリソースに不正なデータが含まれている場合。
138                 * @throws ClassCastException                   ロードされたクラスをResourceBundleにキャストできない場合
139                 * @throws IllegalAccessException               クラスまたはその引数なしのコンストラクタにアクセスできない場合。
140                 * @throws InstantiationException               クラスのインスタンス化が何かほかの理由で失敗する場合。
141                 * @throws ExceptionInInitializerError  このメソッドによる初期化に失敗した場合。
142                 * @throws SecurityException                    セキュリティ・マネージャが存在し、新しいインスタンスの作成が拒否された場合。詳細は、Class.newInstance()を参照してください。
143                 * @throws IOException                                  何らかの入出力操作を使ってリソースを読み取る際にエラーが発生した場合
144                 */
145                @Override               // ResourceBundle.Control
146                public ResourceBundle newBundle( final String baseName,
147                                                                                 final Locale locale,
148                                                                                 final String format,
149                                                                                 final ClassLoader loader,
150                                                                                 final boolean reload ) throws IllegalAccessException, InstantiationException, IOException {
151                        // The below is a copy of the default implementation.
152                        final String bundleName   = toBundleName(   baseName  , locale );
153                        final String resourceName = toResourceName( bundleName, "properties" );
154                        InputStream stream = null;
155                        if( reload ) {
156                                final URL url = loader.getResource( resourceName );
157                                if( url != null ) {
158                                        final URLConnection connection = url.openConnection();
159                                        if( connection != null ) {
160                                                connection.setUseCaches( false );
161                                                stream = connection.getInputStream();
162                                        }
163                                }
164                        } else {
165                                stream = loader.getResourceAsStream( resourceName );
166                        }
167
168                        ResourceBundle bundle = null;
169                        if( stream != null ) {
170                                try {
171                                        // Only this line is changed to make it to read properties files as UTF-8.
172                                        bundle = new PropertyResourceBundle( new BufferedReader( new InputStreamReader( stream,CHARSET ) ) );
173                                } finally {
174                                        stream.close();
175                                }
176                        }
177                        return bundle;
178                }
179        };
180}