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.hayabusa.resource;
017
018import java.util.Arrays;
019import java.util.Collections;
020import java.util.HashMap;
021import java.util.HashSet;
022import java.util.LinkedHashMap;
023import java.util.Map;
024import java.util.Set;
025
026import org.opengion.fukurou.util.ErrMsg;
027import org.opengion.fukurou.util.StringUtil;
028import org.opengion.hayabusa.common.HybsSystem;
029import org.opengion.hayabusa.db.DBColumn;
030import org.opengion.hayabusa.db.DBColumnConfig;
031
032/**
033 * java.util.ResourceBundle クラスを複数管理するリソースクラスです。
034 *
035 * ResourceManager は、
036 *      LabelResource.properties   ラベルリソース(テーブル定義やカラム名などの画面に表示するリソース)
037 *      CodeResource.properties    コードリソース(選択データなどプルダウンメニューで選択するリソース)
038 *      MessageResource.properties メッセージリソース(エラーコードやメッセージなどを表示するリソース)
039 *
040 * の3つのプロパティーファイルを内部に持っており,それぞれのメソッドにより,
041 * リソースの返す値を決めています。
042 *
043 * ResourceManagerは,単独でも生成できますが,各ユーザー毎に作成するよりも
044 * ResourceFactory#newInstance( lang )メソッドより生成した方が,プーリングされるので
045 * 効率的です。
046 *
047 * リソース作成時に指定するロケールは,ISO 言語コード(ISO-639 で定義される 2 桁の小文字)
048 * <a href ="http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt">
049 * http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt</a>を使用して下さい。
050 * ただし,内部的に Locale を構築していますが,その正しさは,チェックされていませんので,
051 * 指定するロケールに応じた properties ファイルを用意しておいて下さい。
052 *
053 * 日本語の場合は, 言語コードは "jp" なので,
054 *      LabelResource_jp.properties   ラベルリソース(日本語)
055 *      CodeResource_jp.properties    コードリソース(日本語)
056 *      MessageResource_jp.properties メッセージリソース(日本語)
057 *
058 * を用意して下さい。
059 *
060 * CodeResource については、リソースファイルから CodeSelectionオブジェクトを
061 * 作成して利用します。この、CodeSelectionオブジェクトの作成方法として、
062 * 3通り考えられます。
063 * 1つ目は、毎回 要求が発生する毎に CodeSelection を作成し、プールしていきます。こうすることで、
064 * 初めて使用されたときだけオブジェクト化されますので、メモリの節約が可能です。ただし、
065 * プールにヒットしなかった場合は、やはりリソースから検索しますので、元々ヒットしない
066 * キーに対しては、毎回リソースを検索するため、非効率です。
067 * 2つめは、元々ヒットしないキーに対して、NullCodeSelectionオブジェクトを登録しておくことで、
068 * プールにため込んで行くと言う方法です。この場合は、シングルトーンにしてメモリを節約しますが、
069 * それでもプール自体の容量は、確保しておく必要があります。
070 * 3つめは、この ResourceManager がインスタンス化されるときに、すべての CodeSelectionオブジェクトを
071 * あらかじめ プールしておく方法です。使わない CodeSelection もインスタンス化する変わりに、
072 * キャッシュにヒットしない場合は、即 CodeSelection が存在しないと判断できるため、
073 * もっともパフォーマンスが高くなります。
074 * 本 ResourceManager の実装は、3つめの、あらかじめ、すべてをキャッシュしておく方法を
075 * 採用しています。
076 *
077 * @og.group リソース管理
078 *
079 * @version  4.0
080 * @author       Kazuhiko Hasegawa
081 * @since    JDK5.0,
082 */
083public final class ResourceManager {
084        private final ColumnDataLoader          columnLoader ;
085        private final CodeDataLoader            codeLoader;
086        private final LabelDataLoader           labelLoader;
087        private final GUIDataLoader                     guiLoader;
088
089        private final Map<String,DBColumn> columnPool = Collections.synchronizedMap( new HashMap<String,DBColumn>( HybsSystem.BUFFER_LARGE ) );
090        private final String    lang ;
091
092        /**
093         *      コンストラクター
094         *      システムIDと言語コードを指定して,生成します。
095         *
096         * @param       systemId システムID
097         * @param       lg 言語コード
098         * @param       initLoad リソースデータの先読み可否(true:先読みする)
099         */
100        public ResourceManager( final String systemId,final String lg,final boolean initLoad ) {
101                this.lang         = lg;
102
103                columnLoader    = new ColumnDataLoader( systemId,initLoad );
104                labelLoader             = new LabelDataLoader( systemId,lang,initLoad );
105                codeLoader              = new CodeDataLoader( systemId,initLoad,labelLoader ); // 4.0.0.0(2007/10/17)
106                guiLoader               = new GUIDataLoader( systemId );
107        }
108
109        /**
110         * 設定されている言語を返します。
111         *
112         * @return      言語
113         */
114        public String getLang() {
115                return lang;
116        }
117
118        /**
119         * DBColumn オブジェクトを取得します。
120         * 作成したDBColumnオブジェクトは,内部にプールしておき,同じオブジェクト要求が
121         * あったときは,プールのオブジェクトを利用して,DBColumnを返します。
122         *
123         * @og.rev 3.4.0.0 (2003/09/01) ラベルカラム、コードカラム、表示パラメータ、編集パラメータ、文字パラメータの追加。
124         * @og.rev 3.5.6.4 (2004/07/16) 追加パラメータ取り込み時に、"_" は、null 扱いとする。
125         * @og.rev 3.6.0.7 (2004/11/06) DBColumn の official属性追加
126         *
127         * @param       key     カラムID
128         *
129         * @return      DBColumnオブジェクト
130         */
131        public DBColumn getDBColumn( final String key ) {
132                DBColumn clm = columnPool.get( key );
133                if( clm == null ) {
134                        ColumnData clmDt = columnLoader.getColumnData( key );
135                        if( clmDt != null ) {
136                                String label_clm = clmDt.getLabelColumn();
137                                String code_clm  = clmDt.getCodeColumn();
138
139                                clm = new DBColumn(
140                                                        lang,
141                                                        clmDt,
142                                                        labelLoader.getLabelData( label_clm ),
143                                                        codeLoader.getCodeData( code_clm ) );
144
145                                columnPool.put( key,clm );
146                        }
147                }
148                return clm;
149        }
150
151        /**
152         * DBColumn オブジェクトを作成します。
153         * 内部にプールに存在すればそれを、なければ新規に作成します。
154         * それでも存在しない場合は、DBColumnConfig より、ラベルと言語を指定して
155         * 新規に作成します。
156         *
157         * @param       key     カラムID
158         *
159         * @return      DBColumnオブジェクト
160         * @see         #getDBColumn( String )
161         */
162        public DBColumn makeDBColumn( final String key ) {
163                DBColumn dbColumn = getDBColumn( key );
164                if( dbColumn == null ) {
165                        DBColumnConfig config = new DBColumnConfig( key );
166                        config.setLabelData( getLabelData( key ) );
167                        config.setLang( getLang() );
168                        dbColumn = new DBColumn( config );
169                }
170                return dbColumn;
171        }
172
173        /**
174         * DBColumn オブジェクトをプールに登録します。
175         * DBColumn を動的に作成する機能で、作成したカラムオブジェクトを
176         * プールに登録することで、通常のリソースと同じように利用できるように
177         * します。
178         *
179         * @og.rev 5.4.2.2 (2011/12/14) 新規追加
180         *
181         * @param       key  カラムID
182         * @param       dbColumn DBColumnオブジェクト
183         */
184        public void setDBColumn( final String key , final DBColumn dbColumn ) {
185                if( key != null && key.length() > 0 && dbColumn != null ) {
186                        columnPool.put( key,dbColumn );
187                }
188        }
189
190        /**
191         * ラベルリソースから,ラベルを返します。
192         * 引数の言語コードに応じたリソースが登録されていない場合は,
193         * 引数のラベルキーそのまま返します。
194         *
195         * @og.rev 4.0.0.0 (2005/01/31) オラクルとWindowsとの間の "~"の文字化け対策中止
196         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソースとの統合化
197         *
198         * @param       key ラベルキー
199         *
200         * @return      リソースに応じたラベル文字列(無ければ ラベルキー)
201         */
202        public String getLabel( final String key ) {
203                LabelData lblData = labelLoader.getLabelData( key );
204                if( lblData != null ) {
205                        String rtn = lblData.getLabel();
206                        if( rtn != null ) {
207                                return rtn;
208                        }
209                }
210
211                // なければ key を返す
212                return key;
213        }
214
215        /**
216         * メッセージリソースから,キーで指定されたメッセージに,
217         * 引数で指定された変数値をセットしたメッセージを返します。
218         *
219         * このメッセージは,リソースで選ばれたロケール毎のメッセージに,
220         * MessageFormat#format でフォーマットする事により,作成されます。
221         * メッセージがリソースに存在しない場合は,キーを返します。
222         *
223         * @og.rev 4.0.0.0 (2005/01/31) オラクルとWindowsとの間の "~"の文字化け対策
224         * @og.rev 4.0.0.0 (2007/10/17) メッセージリソース統合に伴いラベルローダーを使用する
225         * @og.rev 4.0.0.0 (2007/10/18) 名称変更 getMessage ⇒ getLabel
226         * @og.rev 5.1.1.0 (2009/12/01) #xxxxの変換で、カラム名が複数指定されている場合の対応
227         *
228         * @param       key キー
229         * @param       args メッセージの引数
230         *
231         * @return      メッセージ(無ければ キー)
232         */
233        public String getLabel( final String key,final String[] args ) {
234                final String msglbl ;
235
236                if( args == null ) {
237                        msglbl = getLabel( key );
238                }
239                else {
240                        LabelData msgDt = labelLoader.getLabelData( key );
241
242                        int size = args.length;
243                        String[] msgArgs = new String[size];
244                        for( int i=0; i<size; i++ ) {
245                                String arg = args[i] ;
246                                if( arg != null && arg.startsWith( "#" ) ) {
247                                        if( arg.indexOf( ',' ) < 0 ) {
248                                                msgArgs[i] = getLabel( arg.substring( 1 ) );
249                                        }
250                                        // 5.1.1.0 (2009/12/01) #CLM,LANG,KBSAKU 等項目名が複数指定できるようにする
251                                        else {
252                                                String[] argArr = StringUtil.csv2Array( arg.substring( 1 ) );
253                                                StringBuilder argBuf = new StringBuilder();
254                                                for( int j=0; j<argArr.length; j++ ) {
255                                                        if( j > 0 ) {
256                                                                argBuf.append( ',' );
257                                                        }
258                                                        argBuf.append( getLabel( argArr[j]) );
259                                                }
260                                                msgArgs[i] = getLabel( argBuf.toString() );
261                                        }
262                                }
263                                else {
264                                        msgArgs[i] = arg ;
265                                }
266                        }
267
268                        msglbl = msgDt.getMessage( msgArgs );
269                }
270
271                return msglbl;
272        }
273
274        /**
275         * メッセージリソースから,ErrMsgオブジェクトで指定されたメッセージを返します。
276         *
277         * このエラーメッセージは,リソースで選ばれたロケール毎のメッセージに,
278         * MessageFormat#format でフォーマットする事により,作成されます。
279         * エラーメッセージがリソースに存在しない場合は,エラーコードを返します。
280         *
281         * @og.rev 4.0.0.0 (2004/12/31) 新規追加
282         * @og.rev 4.0.0.0 (2007/10/18) メッセージリソースとの統合化
283         *
284         * @param       errMsg ErrMsgオブジェクト
285         *
286         * @return      エラーメッセージ(無ければ ErrMsgオブジェクトの toString() )
287         */
288        public String getLabel( final ErrMsg errMsg ) {
289                String   key  = errMsg.getId();
290                String[] args = errMsg.getArgs();
291
292                return getLabel( key,args );
293        }
294
295        /**
296         * ラベルリソースから,ラベル(短)を返します。
297         * 引数の言語コードに応じたリソースが登録されていない場合は,
298         * 引数のラベルキーそのまま返します。
299         *
300         * @og.rev 4.3.3.0 (2008/10/01) 新規作成
301         *
302         * @param       key ラベルキー
303         *
304         * @return      リソースに応じたラベル文字列(無ければ ラベルキー)
305         */
306        public String getShortLabel( final String key ) {
307                LabelData lblData = labelLoader.getLabelData( key );
308                if( lblData != null ) {
309                        String rtn = lblData.getShortLabel();
310                        if( rtn != null ) {
311                                return rtn;
312                        }
313                }
314
315                // なければ key を返す
316                return key;
317        }
318
319        /**
320         * ラベルリソースから,概要説明を返します。
321         * キーのデータが存在しない場合はnullを返します。
322         *
323         * @og.rev 4.3.4.5 (2009/01/08) 新規作成
324         *
325         * @param       key ラベルキー
326         *
327         * @return      リソースに応じた概要説明(無ければ null)
328         */
329        public String getDescription( final String key ) {
330                LabelData lblData = labelLoader.getLabelData( key );
331                if( lblData != null ) {
332                        String rtn = lblData.getDescription();
333                        if( rtn != null ) {
334                                return rtn;
335                        }
336                }
337                // キーが存在しなければnullで返す
338                return null;
339        }
340
341        /**
342         * ラベルリソースから,概要説明を返します。
343         * {0},{1}...の置換えを行います。
344         * キーのデータが存在しない場合はnullを返します。
345         *
346         * @og.rev 4.3.7.6 (2009/07/15) 新規作成
347         *
348         * @param       key ラベルキー
349         * @param       args パラメータ
350         *
351         * @return      リソースに応じた概要説明(無ければ null)
352         */
353        public String getDescription( final String key, final String[] args ) {
354                String rtn = null;
355                if( args == null ){
356                        rtn = getDescription( key );
357                }
358                else{
359                        LabelData lblData = labelLoader.getLabelData( key );
360                        if( lblData != null ) {
361                                int size = args.length;
362                                String[] msgArgs = new String[size];
363                                for( int i=0; i<size; i++ ) {
364                                        String arg = args[i] ;
365                                        if( arg != null && arg.startsWith( "#" ) ) {
366                                                msgArgs[i] = getLabel( arg.substring( 1 ) );
367                                        }
368                                        else {
369                                                msgArgs[i] = arg ;
370                                        }
371                                }
372                                rtn = lblData.getDescription( msgArgs );
373                        }
374                }
375                // キーが存在しなければnullで返る
376                return rtn;
377        }
378
379        /**
380         * ラベルリソースから,概要説明を返します。
381         * キーのデータが存在しない場合はnullを返します。
382         *
383         * @og.rev 4.3.7.6 (2009/07/15) 新規作成
384         *
385         * @param       errMsg ErrMsgオブジェクト
386         *
387         * @return      エラーメッセージ(キーが無ければnull)
388         */
389        public String getDescription( final ErrMsg errMsg ) {
390                String   key  = errMsg.getId();
391                String[] args = errMsg.getArgs();
392
393                return getDescription( key,args );
394        }
395
396        /**
397         * ラベルリソースから,ラベルを返します。
398         * 引数の言語コードに応じたリソースが登録されていない場合は,
399         * 引数のラベルキーそのまま返します。
400         *
401         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
402         *
403         * @param       key ラベルキー
404         *
405         * @return      リソースに応じたラベル文字列(無ければ ラベルキー)
406         */
407        public LabelData getLabelData( final String key ) {
408                return labelLoader.getLabelData( key );
409        }
410
411        /**
412         * コードリソースから,コード文字列を返します。
413         *
414         * @param       key コードキー
415         *
416         * @return      コードデータオブジェクト(無ければ null)
417         */
418        public CodeData getCodeData( final String key ) {
419                return codeLoader.getCodeData( key );
420        }
421
422        /**
423         * コードリソースから,コード文字列を返します。
424         * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。
425         *
426         * @og.rev 5.4.2.2 (2011/12/14) 新規追加。
427         *
428         * @param       key コードキー
429         * @param       query 検索SQL(引数に、? を一つ持つ)
430         *
431         * @return      コードデータオブジェクト(無ければ null)
432         */
433        public CodeData getCodeData( final String key,final String query ) {
434                return codeLoader.getCodeData( key,query );
435        }
436
437        /**
438         * ログインユーザーで使用する画面オブジェクトを、UserInfoにセットします。
439         * 各、UserInfo は、自分自身が使用する 画面オブジェクトのみを管理することで、
440         * 画面アクセス有無を、すばやく検索することが可能になります。
441         *
442         * @og.rev 3.1.0.1 (2003/03/26) GUIInfo のキー順サポートの為に、引数追加。
443         * @og.rev 4.0.0.0 (2005/01/31) 使用画面のMap を UserInfo にセットします。
444         * @og.rev 4.3.0.0 (2008/07/04) ロールモードマルチ対応
445         * @og.rev 5.2.0.0 (2010/09/01) アクセス禁止アドレスによる不正アクセス防止機能追加
446         *
447         * @param       user    指定のユーザーロールに対応する画面だけをMapにセットする。
448         */
449        public void makeGUIInfos( final UserInfo user ) {
450                GUIData[] guiDatas = guiLoader.getAllData();
451
452                // guikey に対してユニークになるように Map に追加します。後登録が優先されます。
453                Map<String,GUIInfo> guiMap = new HashMap<String,GUIInfo>();
454                Set<String> forbidAddrSet = new HashSet<String>();
455                int size = guiDatas.length;
456                for( int i=0; i<size; i++ ) {
457                        GUIData gui = guiDatas[i];
458                        byte bitMode = user.getAccessBitMode( gui.getRoleMode() );
459                        if(  bitMode > 0 ) {
460                                String    guikey        = gui.getGuiKey();
461                                LabelData labelData = getLabelData( gui.getLabelClm() );
462                                guiMap.put( guikey,new GUIInfo( gui,labelData,bitMode ) );
463                        }
464                        // 5.2.0.0 (2010/09/01) アクセス禁止アドレスによる不正アクセス防止機能追加
465                        else {
466                                String addr = gui.getAddress();
467                                if( addr.indexOf( '/' ) < 0 ) {
468                                        forbidAddrSet.add( addr );
469                                }
470                        }
471                }
472
473                // もし、禁止リストの中に画面ID違いで許可リストと同じアドレスが
474                // 含まれている場合は、禁止リスト中から該当のアドレスを削除する。
475                for( GUIInfo gui : guiMap.values() ) {
476                        String addr = gui.getAddress();
477                        if( forbidAddrSet.contains( gui.getAddress() ) ) {
478                                forbidAddrSet.remove( addr );
479                        }
480                }
481
482                // GUIInfo をその順番(SEQNO順)でソートし直します。
483                GUIInfo[] guiInfos = guiMap.values().toArray( new GUIInfo[ guiMap.size() ] ) ;
484                Arrays.sort( guiInfos );
485                Map<String,GUIInfo> sortMap = new LinkedHashMap<String,GUIInfo>();
486                size = guiInfos.length;
487                for( int i=0; i<size; i++ ) {
488                        GUIInfo guiInfo = guiInfos[i];
489                        String guikey   = guiInfo.getKey();
490                        sortMap.put( guikey,guiInfo );
491                }
492
493                user.setGUIMap( sortMap, forbidAddrSet );
494        }
495
496        /**
497         * 指定されたクエリを発行し、ラベルマップを作成します。
498         *
499         * @og.rev 4.3.4.0 (2008/12/01) 新規作成
500         *
501         * @param       query   ラベルマップを作成するクエリ
502         *
503         * @return      ラベルマップ
504         * @see org.opengion.hayabusa.resource.LabelDataLoader#getLabelMap( String )
505         */
506        public Map<String, LabelData> getLabelMap( final String query ) {
507                return labelLoader.getLabelMap( query );
508        }
509
510        /**
511         * リソースマネージャーをキーに基づいて部分クリアします。
512         * ここでは、部分クリアなため、GUIData に関しては、処理されません。
513         * また、存在しないキーを指定されたリソースは、何も処理されません。
514         *
515         * @og.rev 5.4.3.4 (2012/01/12) labelPool の削除追加
516         *
517         * @param   key         カラムのキー
518         */
519        public void clear( final String key ) {
520                System.out.println( "Key=[" + key + "] の部分リソースクリアを実施しました。" );
521                columnLoader.clear( key );
522                codeLoader.clear( key );
523                labelLoader.clear( key );
524                columnPool.remove( key );
525        }
526
527        /**
528         * GUI情報をクリアします。
529         * ここでは、関連するラベル、コードリソースの部分クリアも行います。
530         * GUI情報は、シーケンスに管理しているため、この処理1回ごとに、
531         * GUIData を全件再読み込みを行いますので、ご注意ください。
532         *
533         */
534        public void guiClear() {
535                GUIData[] gui = guiLoader.getAllData();
536
537                for( int i=0; i<gui.length; i++ ) {
538                        String key = gui[i].getGuiKey();
539                        labelLoader.clear( key );
540                }
541                codeLoader.clear( "CLASSIFY" );
542                guiLoader.clear();
543        }
544
545        /**
546         * リソースマネージャーをクリア(初期化)します。
547         *
548         * @og.rev 5.4.3.4 (2012/01/12) labelPool の削除追加
549         *
550         */
551        public void clear() {
552                columnLoader.clear();
553                codeLoader.clear();
554                labelLoader.clear();
555                guiLoader.clear();
556                columnPool.clear();
557        }
558}