    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 * の３つのプロパティーファイルを内部に持っており,それぞれのメソッドにより，
    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 * ３通り考えられます。
    063 * １つ目は、毎回 要求が発生する毎に CodeSelection を作成し、プールしていきます。こうすることで、
    064 * 初めて使用されたときだけオブジェクト化されますので、メモリの節約が可能です。ただし、
    065 * プールにヒットしなかった場合は、やはりリソースから検索しますので、元々ヒットしない
    066 * キーに対しては、毎回リソースを検索するため、非効率です。
    067 * ２つめは、元々ヒットしないキーに対して、NullCodeSelectionオブジェクトを登録しておくことで、
    068 * プールにため込んで行くと言う方法です。この場合は、シングルトーンにしてメモリを節約しますが、
    069 * それでもプール自体の容量は、確保しておく必要があります。
    070 * ３つめは、この ResourceManager がインスタンス化されるときに、すべての CodeSelectionオブジェクトを
    071 * あらかじめ プールしておく方法です。使わない CodeSelection もインスタンス化する変わりに、
    072 * キャッシュにヒットしない場合は、即 CodeSelection が存在しないと判断できるため、
    073 * もっともパフォーマンスが高くなります。
    074 * 本 ResourceManager の実装は、３つめの、あらかじめ、すべてをキャッシュしておく方法を
    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         *      システムＩＤと言語コードを指定して,生成します。
    095         *
    096         * @param       systemId システムＩＤ
    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情報は、シーケンスに管理しているため、この処理１回ごとに、
    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}