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 org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.fukurou.util.ApplicationInfo;
020import org.opengion.fukurou.db.DBUtil;
021
022import java.util.Map;
023import java.util.HashMap;
024import java.util.LinkedHashMap ;
025import java.util.WeakHashMap ;
026import java.util.Collections ;
027
028/**
029 * コードオブジェクトを作成するデータロードクラスです。
030 * systemId と lang に対応したコードオブジェクトを作成します。
031 *
032 * コードオブジェクトは、項目(CLM)に対して、複数のコード(CODE)を持っています。
033 * この複数のコードを表示順に持つことで、プルダウンメニュー等の表示順を指定します。
034 *
035 * コードオブジェクトを作成する場合は、同一項目・コードで、作成区分(KBSAKU)違いの場合は、
036 * 最も大きな作成区分を持つコードを使用します。
037 * 作成区分(KBSAKU)は、他のリソースと異なり、同一項目・コード単位に設定すべきです。
038 * これは、通常は項目単位に作成区分を持つべきところを、コード単位でしか
039 * 持てないデータベースの設計になっている為です。アプリケーション側で設定条件を
040 * きちんと管理すれば、作成区分を使用できますが、一般にはお奨めできません。
041 * 作成区分(KBSAKU)='0' のデータは、マスタリソースとして、エンジンとともに
042 * 配布されるリソースになります。
043 *
044 * 読み込みフラグ(FGLOAD)は、使用しません。
045 * コードリソースに関しては、システム起動時に、すべてのコードリソースをエンジン内部
046 * に取り込みます。ただし、リソースのキャッシュに、WeakHashMap クラスを使用しているため、
047 * メモリオーバー時には、クリアされるため、単独での読み取りも行います。
048 * SYSTEM_ID='**' は、共通リソースです。
049 * これは、システム間で共通に使用されるリソース情報を登録しておきます。
050 *
051 * @og.rev 4.0.0.0 (2004/12/31) 新規作成
052 * @og.group リソース管理
053 *
054 * @version  4.0
055 * @author   Kazuhiko Hasegawa
056 * @since    JDK5.0,
057 */
058final class CodeDataLoader {
059        // リソースの接続先を、取得します。
060        private final String DBID = HybsSystem.sys( "RESOURCE_DBID" );
061
062        /** DBリソースの初期一括読み込みのクエリー */
063        // キーブレイクで、SYSTEM_ID 違いは、まとめて処理する為、最初に ORDER BY しておく必要があります。
064        // 5.1.9.0 (2010/08/01) order by 変更
065        public static final String QUERY = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)      
066                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1'"
067                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
068
069        /** DBリソースの個別読み込み時のクエリー */
070        // 5.1.9.0 (2010/08/01) order by 変更
071        public static final String QUERY2 = "select CLM,CODE,'','',CODELVL,CODEGRP,CODE_PARAM,ROLES,SYSTEM_ID,KBSAKU,'',''" // 5.6.8.2 (2013/09/20)
072                                                                        + " from GEA04 where SYSTEM_ID in ( ?,'**') and FGJ='1' and CLM=?"
073                                                                        + " order by SYSTEM_ID,KBSAKU,CLM,SEQNO,CODELVL,CODE" ;
074
075        private final Map<String,CodeData> pool = Collections.synchronizedMap( new WeakHashMap<String,CodeData>() );    // キャッシュ用プール
076        private final String  SYSTEM_ID ;               // システムID
077
078        /** コネクションにアプリケーション情報を追記するかどうか指定 */
079        public static final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
080
081        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
082        private final ApplicationInfo appInfo;
083
084        private final LabelDataLoader LABEL_LOADER; // 見直し要!!!
085
086        /**
087         *  lang 毎に ファクトリオブジェクトを作成します。
088         *
089         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
090         *
091         * @param systemId システムID
092         * @param initLoad リソースデータの先読み可否(true:先読みする)
093         * @param lLoader ラベルデータローダー
094         */
095        CodeDataLoader( final String systemId,final boolean initLoad,final LabelDataLoader lLoader) {
096                SYSTEM_ID = systemId;
097                LABEL_LOADER = lLoader;
098
099                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
100                if( USE_DB_APPLICATION_INFO ) {
101                        appInfo = new ApplicationInfo();
102                        // ユーザーID,IPアドレス,ホスト名
103                        appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
104                        // 画面ID,操作,プログラムID
105                        appInfo.setModuleInfo( "CodeDataLoader",null,null );
106                }
107                else {
108                        appInfo = null;
109                }
110
111                // ApplicationInfo の設定が終わってから実行します。
112                if( initLoad ) { loadDBResource(); }
113        }
114
115        /**
116         * DBリソースより コードデータを取得、設定します。
117         *
118         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
119         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
120         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel対応
121         */
122        private void loadDBResource() {
123                String[] args = new String[] { SYSTEM_ID };
124
125                String[][] vals = DBUtil.dbExecute( QUERY,args,appInfo,DBID );
126
127                Map<String,Map<String,String[]>> clmMap  = new HashMap<String,Map<String,String[]>>();
128                int len = vals.length;
129                String bkClm = null;                    // キーブレイク
130                String bkSystem = null;
131                String bkKbsaku = null;
132                // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
133                Map<String,String[]> codeMap = null;
134                for( int i=0; i<len; i++ ) {
135                        String   clm  = vals[i][CodeData.CLM];
136                        String   code = vals[i][CodeData.CODE];
137                        String   systemId = vals[i][CodeData.SYSTEM_ID];
138                        String   kbsaku = vals[i][CodeData.KBSAKU];
139                        if( bkClm == null || !bkClm.equals( clm ) || !bkSystem.equals(systemId) || !bkKbsaku.equals(kbsaku) ) {
140                                codeMap = new LinkedHashMap<String,String[]>();
141                                clmMap.put( clm,codeMap );
142                                bkClm = clm;
143                                bkSystem = systemId;
144                                bkKbsaku = kbsaku;
145                        }
146
147                        String lkey = clm+"."+code; // やっつけ~
148                        vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
149                        vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
150                        vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
151                        vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/01) 加工していない名前長
152                        
153                        codeMap.put( code,vals[i] );
154                }
155
156                String[] clmKeys = clmMap.keySet().toArray( new String[clmMap.size()] );
157                int size = clmKeys.length;
158                for( int i=0; i<size; i++ ) {
159                        String clm = clmKeys[i];
160                        codeMap = clmMap.get( clm );
161
162                        pool.put( clm,new CodeData( clm,codeMap ) );
163                }
164
165                System.out.println( "  CodeDataLoader [" + size + "] loaded" );
166        }
167
168        /**
169         * CodeData オブジェクトを取得します。
170         * 作成したCodeDataオブジェクトは,内部にプールしておき,同じリソース要求が
171         * あったときは,プールの CodeDataを返します。
172         *
173         * @og.rev 4.3.8.0 (2009/08/01) rawShortLabel追加
174         * @og.rev 5.6.8.2 (2013/09/20) rawLongLabel追加
175         *
176         * @param   key       コードのキー
177         *
178         * @return  CodeData  オブジェクト
179         */
180        public CodeData getCodeData( final String key ) {
181                CodeData codeData = pool.get( key ) ;
182
183                if( codeData == null ) {
184                        String[] args = new String[] { SYSTEM_ID,key };
185                        String[][] vals = DBUtil.dbExecute( QUERY2,args,appInfo,DBID );
186
187                        int len = vals.length;
188                        String bkSystem = null;                 // キーブレイク
189                        String bkKbsaku = null;
190                        // 以下の処理は、SYSTEM_ID違いを塊で処理します。(混在させません。)
191                        Map<String,String[]> codeMap = null;
192                        for( int i=0; i<len; i++ ) {
193                                String   systemId = vals[i][CodeData.SYSTEM_ID];
194                                String   code     = vals[i][CodeData.CODE];
195                                String   kbsaku = vals[i][CodeData.KBSAKU];
196                                if( bkSystem == null || !bkSystem.equals( systemId ) || !bkKbsaku.equals(kbsaku) ) {
197                                        codeMap = new LinkedHashMap<String,String[]>();
198                                        bkSystem = systemId;
199                                        bkKbsaku = kbsaku;
200                                }
201
202                                String lkey = key+"."+code; // やっつけ~
203                                vals[i][CodeData.LNAME] = LABEL_LOADER.getLabelData(lkey).getLongLabel();
204                                vals[i][CodeData.SNAME] = LABEL_LOADER.getLabelData(lkey).getShortLabel();
205                                vals[i][CodeData.RSNAME] = LABEL_LOADER.getLabelData(lkey).getRawShortLabel(); // 4.3.8.0 (2009/08/01) spanが付かない名前短
206                                vals[i][CodeData.RLNAME] = LABEL_LOADER.getLabelData(lkey).getRawLongLabel(); // 5.6.8.2 (2013/09/20) 加工していない名前長
207
208                                codeMap.put( code,vals[i] );
209                        }
210
211                        if( codeMap != null ) {
212                                codeData = new CodeData( key,codeMap );
213                                pool.put( key,codeData );
214                        }
215                }
216                return codeData ;
217        }
218
219        /**
220         * CodeData オブジェクトを取得します。
221         * 作成したCodeDataオブジェクトは,内部にプールしておき,同じリソース要求が
222         * あったときは,プールの CodeDataを返します。
223         *
224         * 引数にQUERYを渡すことで、DBから、動的にコードリソースを作成できます。
225         * 引数の順番は、CodeData で定義している CLM,CODE,LNAME,SNAME の順番のままです。
226         * QUERY には、key を引数にとる必要があります。つまり、WHERE CLM = ? の様な記述が必要です。
227         *
228         * @og.rev 5.4.2.2 (2011/12/14) 新規追加。
229         *
230         * @param   key   コードのキー
231         * @param       query 検索SQL(引数に、? を一つ持つ)
232         *
233         * @return  CodeData  オブジェクト
234         */
235        public CodeData getCodeData( final String key,final String query ) {
236                CodeData codeData = pool.get( key ) ;
237
238                if( codeData == null ) {
239                        String[] args = new String[] { key };
240                        String[][] vals = DBUtil.dbExecute( query,args,appInfo,DBID );
241
242                        int len = vals.length;
243                        Map<String,String[]> codeMap = new LinkedHashMap<String,String[]>();
244                        for( int i=0; i<len; i++ ) {
245                                String[] cdVals = new String[CodeData.MAX_LENGTH];      // 空の配列を毎回作成
246
247                                String   code   = vals[i][CodeData.CODE];
248
249                                cdVals[CodeData.CLM]   = key ;
250                                cdVals[CodeData.CODE]  = code;
251                                cdVals[CodeData.LNAME] = vals[i][CodeData.LNAME];
252                                cdVals[CodeData.SNAME] = vals[i][CodeData.SNAME];
253
254                                codeMap.put( code,cdVals );
255                        }
256
257                        if( ! codeMap.isEmpty() ) {
258                                codeData = new CodeData( key,codeMap );
259                                pool.put( key,codeData );
260                        }
261                }
262                return codeData ;
263        }
264
265        /**
266         * CodeData オブジェクトのキャッシュを個別にクリアします。
267         * リソースデータの更新など、一部分の更新時に、すべてのキャッシュを
268         * 破棄するのではなく、指定の分のみ破棄できる機能です。
269         *
270         * @og.rev 4.0.2.0 (2007/12/25) コードリソースクリア時に対応するラベルリソースもクリアする。
271         *
272         * @param   key       コードのキー
273         */
274        public void clear( final String key ) {
275
276                // 4.0.2.0 (2007/12/25)
277                CodeData cdata = pool.remove( key );
278                if( cdata != null ) {
279                        String clm = cdata.getColumn();
280                        for( int i=0; i<cdata.getSize(); i++ ) {
281                                LABEL_LOADER.clear( clm + '.' + cdata.getCodeKey( i ) );
282                        }
283                }
284        }
285
286        /**
287         * CodeData オブジェクトのキャッシュをクリアして、再作成します。
288         *
289         */
290        public void clear() {
291                pool.clear();
292        }
293}