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.db;
017
018import org.opengion.hayabusa.common.HybsSystem;
019import org.opengion.fukurou.util.LogWriter;
020import org.opengion.hayabusa.resource.ResourceFactory;
021import org.opengion.hayabusa.resource.ResourceManager;
022import org.opengion.fukurou.db.DBUtil;
023import org.opengion.fukurou.util.ApplicationInfo;
024
025import java.util.Map;
026import java.util.HashSet;
027import java.util.HashMap;
028
029/**
030 * データのコード情報を取り扱うクラスです。
031 *
032 * コードのキーとラベルの情報から、HTMLのメニューやリストを作成するための オプション
033 * タグを作成したり、与えられたキーをもとに、チェック済みのオプションタグを作成したり
034 * します。
035 * QUERYの第1カラムは、選択キーになります。第2カラムはラベルです。ここまでは必須です。
036 * 第3カラムが存在する場合は、短縮カラムとして認識されます。存在しない場合は、
037 * 短縮ラベルは使用しません。
038 *
039 * メニュー作成用に、SELECT文を与えます。
040 * SELECT 値,ラベル[,グループ][,クラス] FROM XXXX で指定され、値、ラベルまでは必須、
041 * グループは、optgroup に対して指定するラベルです。クラスは、そのオプションに
042 * 色づけなどを行う為の指定です。
043 * なお、グループ、クラス は、NULL(または、ゼロ文字列)の場合は、適用されません。)
044 *
045 * @og.group 選択データ制御
046 *
047 * @version  4.0
048 * @author   Kazuhiko Hasegawa
049 * @since    JDK5.0,
050 */
051// public class Selection_DB implements Selection {
052public class Selection_DB extends Selection_NULL {
053        // 3.5.4.8 (2004/02/23) USE_MULTI_KEY_SELECT を定義しておきます。
054        private final boolean USE_MULTI_KEY_SELECT      = HybsSystem.sysBool( "USE_MULTI_KEY_SELECT" ) ;
055        private final long        DB_CACHE_TIME                 = (long)HybsSystem.sysInt( "DB_CACHE_TIME" ) ;
056
057        private final boolean   isMultiSelect ;
058        private final boolean   isShortLavel ;          // 短縮ラベルを使用できるかどうか
059        private final long              createTime ;            // キャッシュの破棄タイミングを計るための作成時間
060
061        private final int[]             ADRS  ;
062        private final String    CACHE ;
063        private final int               LEN      ;
064        private final int[]             LADRS  ; // 5.1.3.0 (2010/02/01)
065        private final String    LCACHE ; // 5.1.3.0 (2010/02/01)
066        private final int               LLEN   ; // 5.1.3.0 (2010/02/01)
067        private final Map<String,Integer>       map  ;
068
069        private final String[]  value ;         // 値
070        private final String[]  label ;         // ラベル
071        private final String[]  slabel ;        // 短縮ラベル
072
073        private static final int VAL  = 0;
074        private static final int LBL  = 1;
075        private static final int SLBL = 2;
076        private static final int GRP  = 3;
077        private static final int CLS  = 4;
078
079        // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
080        private static final ApplicationInfo appInfo;
081        static {
082                /** コネクションにアプリケーション情報を追記するかどうか指定 */
083                boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
084
085                // 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
086                if( USE_DB_APPLICATION_INFO ) {
087                        String SYSTEM_ID = HybsSystem.sys( "SYSTEM_ID" );
088                        appInfo = new ApplicationInfo();
089                        // ユーザーID,IPアドレス,ホスト名
090                        appInfo.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
091                        // 画面ID,操作,プログラムID
092                        appInfo.setModuleInfo( "Selection_DB",null,null );
093                }
094                else {
095                        appInfo = null;
096                }
097        }
098
099        /**
100         * コンストラクター
101         *
102         * DB検索用のSQL文を与えて、初期化します。
103         * SQL文は、KEY,LNAME [,SNAME] で、第3項がなければ、LNAME を使用します。
104         * LNAME は、通常の値を返す場合に、SNAME は、一覧表示の値を返す場合に使用します。
105         * 特別に、KEY のみの場合は、lang に基づく ResourceManager からラベルを取得します。
106         * ただし、その場合は、オーナー(SYSTEM_ID)は選べません。
107         *
108         * @og.rev 3.5.4.2 (2003/12/15) コンストラクター 新規追加
109         * @og.rev 3.6.0.9 (2004/12/03) isMultiSelect の判定をラベル部のユニーク度で判定します。
110         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為,ApplicationInfoオブジェクトを設定
111         * @og.rev 3.8.9.2 (2007/07/28) グループと、クラスを追加。Select文の第3、第4引数として指定。
112         * @og.rev 4.0.0.0 (2006/11/15) lang 属性を追加します。
113         * @og.rev 4.3.8.0 (2009/08/01) ツールチップ表示機能追加
114         * @og.rev 5.1.3.0 (2010/02/01) ラベル(短)がnullの場合でも、ラベル(短)で表示されてしまうバグを修正
115         * @og.rev 5.1.3.0 (2010/02/01) 一覧表示以外は、ツールチップ表示しない
116         *
117         * @param newQuery      DB検索(SQL)文字列
118         * @param dbid          データベース接続先ID
119         * @param lang          リソースを使用する場合の言語
120         */
121        public Selection_DB( final String newQuery,final String dbid,final String lang ) {
122
123                String[][] cols = DBUtil.dbExecute( newQuery,null,appInfo,dbid );       // 3.8.7.0 (2006/12/15)
124                int count = cols.length;
125
126                value   = new String[count];
127                label   = new String[count];
128                slabel  = new String[count];
129                ADRS    = new int[count];
130                map             = new HashMap<String,Integer>(count);
131
132                int len = ( count > 0 ) ? cols[0].length : 0 ;
133                isShortLavel  = len > SLBL ;    // >= 3 と同意;
134                boolean isGrp = len > GRP ;             // >= 4 と同意;
135                boolean isCls = len > CLS ;             // >= 5 と同意;
136
137                boolean useLabelData = false ;
138                ResourceManager resource = null;
139                if( len == 1 ) {
140                        useLabelData = true;
141                        resource = ResourceFactory.newInstance( lang );
142                }
143
144                // 3.6.0.9 (2004/12/03)
145                HashSet<String> set = new HashSet<String>();
146                boolean flag = USE_MULTI_KEY_SELECT;    // 判定処理を行う。false なら判定処理も行わない。
147
148                StringBuilder buf = new StringBuilder( HybsSystem.BUFFER_LARGE );
149                String bkGroupKey = "";
150                for( int i=0; i<count; i++ ) {
151                        value[i] = cols[i][VAL];
152                        if( useLabelData ) {
153                                label[i] = resource.getLabel( value[i] );
154                        }
155                        else {
156                                label[i] = cols[i][LBL];
157                                if( isShortLavel ) { slabel[i] = cols[i][SLBL]; }
158                        }
159                        map.put( value[i], Integer.valueOf( i ) );
160
161                        // 3.8.9.2 (2007/07/28)
162                        if( isGrp ) {
163                                String groupKey = cols[i][GRP];
164                                if( !groupKey.equals( bkGroupKey ) ) {  // キーブレイク
165                                        if( ! "".equals( bkGroupKey ) ) {
166                                                buf.append( "</optgroup>" );
167                                        }
168                                        if( ! "".equals( groupKey ) ) {
169                                                buf.append( "<optgroup label=\"" + groupKey + "\">" );
170                                        }
171                                        bkGroupKey = groupKey;
172                                }
173                        }
174
175                        buf.append( "<option value=\"" ).append( value[i] ).append( "\"" );
176                        ADRS[i] = buf.length() ;
177                        if( isCls ) {
178                                String classKey = cols[i][CLS];
179                                if( ! "".equals( classKey ) ) {
180                                        buf.append( " class=\"" ).append( classKey ).append( "\"" );
181                                }
182                        }
183                        // 4.3.8.0 (2009/08/01) slabel利用の場合はlabelをtitle属性にセット
184                        //buf.append( ">" ).append( label[i] ).append( "</option>" );
185                        if( isShortLavel && slabel[i] != null && slabel[i].length() > 0 ){ // 5.1.3.0 (2010/02/01)
186                                if( !label[i].equals( slabel[i] ) ){ // slabelとlabelが異なる場合のみ
187                                        buf.append( " title=\"" ).append( label[i] ).append( "\"" );
188                                }
189                                buf.append( ">" ).append( slabel[i] );
190                        }
191                        else{
192                                buf.append( ">" ).append( label[i] );
193                        }
194                        buf.append( "</option>" );
195
196                        // 3.6.0.9 (2004/12/03)
197                        if( flag && label[i].length() > 0 ) {
198                                flag = set.add( label[i].substring(0,1) );      // 重複時は false
199                        }
200                }
201                if( isGrp && ! "".equals( bkGroupKey ) ) {
202                        buf.append( "</optgroup>" );
203                }
204
205                CACHE = buf.toString();
206                LEN   = CACHE.length() + 30;
207
208                // 5.1.3.0 (2010/02/01) ツールチップ表示が適用されている場合のみ、ツールチップなしの状態のoptionをキャッシュする。
209                if( CACHE.indexOf( "title=\"" ) < 0 ) {
210                        LADRS  = null;
211                        LCACHE = null;
212                        LLEN   = 0;
213                }
214                else {
215                        LADRS  = new int[count];
216                        StringBuilder lbuf = new StringBuilder( HybsSystem.BUFFER_LARGE );
217
218                        bkGroupKey = "";
219                        for( int i=0; i<count; i++ ) {
220                                if( isGrp ) {
221                                        String groupKey = cols[i][GRP];
222                                        if( !groupKey.equals( bkGroupKey ) ) {
223                                                if( ! "".equals( bkGroupKey ) ) { lbuf.append( "</optgroup>" ); }
224                                                if( ! "".equals( groupKey ) )   { lbuf.append( "<optgroup label=\"" + groupKey + "\">" ); }
225                                                bkGroupKey = groupKey;
226                                        }
227                                }
228                                lbuf.append( "<option value=\"" ).append( value[i] ).append( "\"" );
229                                LADRS[i] = lbuf.length() ;
230                                if( isCls && ! "".equals( cols[i][CLS] ) ) {
231                                        lbuf.append( " class=\"" ).append( cols[i][CLS] ).append( "\"" );
232                                }
233                                lbuf.append( ">" ).append( label[i] ).append( "</option>" );
234                        }
235                        if( isGrp && ! "".equals( bkGroupKey ) ) {
236                                lbuf.append( "</optgroup>" );
237                        }
238                        LCACHE = lbuf.toString();
239                        LLEN   = LCACHE.length() + 30;
240                }
241
242                isMultiSelect = USE_MULTI_KEY_SELECT && ! flag ;        // flag の反転に注意
243                createTime = System.currentTimeMillis() ;
244        }
245
246        /**
247         * 初期値が選択済みの 選択肢(オプション)を返します。
248         * このオプションは、引数の値を初期値とするオプションタグを返します。
249         * このメソッドでは、ラベル(短)が設定されている場合でも、これを使用せずに必ずラベル(長)を使用します。
250         *
251         * @og.rev 3.6.0.6 (2004/10/22) シーケンスアクセス機能を指定する seqFlag を導入
252         * @og.rev 4.0.0.0 (2005/01/31) selectValue が、null/ゼロ文字列でない場合に、選択肢にない場合は、エラー
253         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
254         *
255         * @param   selectValue  選択されている値
256         * @param   seqFlag  シーケンスアクセス機能 [true:ON/false:OFF]
257         *
258         * @return  オプションタグ
259         * @see     #getOption( String, boolean, boolean )
260         */
261//      public String getOption( final String selectValue,final boolean seqFlag ) {
262//              return getOption( selectValue, seqFlag, false );
263//      }
264
265        /**
266         * 初期値が選択済みの 選択肢(オプション)を返します。
267         * このオプションは、引数の値を初期値とするオプションタグを返します。
268         * このメソッドでは、引数のuseShortLabelがtrueに指定された場合に、ラベル(短)をベースとした
269         * ツールチップ表示を行います。
270         *
271         * @og.rev 5.1.3.0 (2010/02/01) 追加
272         *
273         * @param   selectValue  選択されている値
274         * @param   seqFlag  シーケンスアクセス機能 [true:ON/false:OFF]
275         * @param   useShortLabel ラベル(短)をベースとしたオプション表示を行うかどうか。
276         *
277         * @return  オプションタグ
278         * @see     #getOption( String, boolean )
279         */
280        @Override
281        public String getOption( final String selectValue,final boolean seqFlag, final boolean useShortLabel ) {
282                int[] adrs = null;
283                String cache = null;
284                int len = 0;
285                if( !useShortLabel && LCACHE != null && LCACHE.length() > 0 ) {
286                        adrs = LADRS;
287                        cache = LCACHE;
288                        len = LLEN;
289                }
290                else {
291                        adrs = ADRS;
292                        cache = CACHE;
293                        len = LEN;
294                }
295
296                // マッチするアドレスを探す。
297                Integer sel = map.get( selectValue );
298
299                if( sel == null ) {
300                        // 4.0.0 (2005/01/31)
301                        if( selectValue != null && selectValue.length() > 0 ) {
302                                String errMsg = "DBコードに存在しない値が指定されました。"
303                                                        + " value=[" + selectValue + "]"
304                                                        + HybsSystem.CR ;
305                                LogWriter.log( errMsg );
306                        }
307                        return cache;
308                }
309                else {
310                        int selected = sel.intValue();
311                        StringBuilder buf = new StringBuilder( len );
312                        // 3.6.0.6 (2004/10/22) シーケンスアクセス機能を指定する seqFlag を導入
313                        if( seqFlag ) {
314                                buf.append( "<option value=\"" ).append( value[selected] ).append( "\"" );
315                        }
316                        else {
317                                buf.append( cache.substring( 0,adrs[selected] ) );
318                        }
319                        buf.append( " selected=\"selected\"" );
320                        buf.append( cache.substring( adrs[selected] ) );
321                        return buf.toString() ;
322                }
323        }
324
325        /**
326         * 初期値が選択済みの 選択肢(オプション)を返します。
327         * このオプションは、引数の値を初期値とするオプションタグを返します。
328         * ※ このクラスでは実装されていません。
329         *
330         * @og.rev 2.1.0.1 (2002/10/17) 選択リストを、正方向にしか選べないようにする sequenceFlag を導入する
331         * @og.rev 3.8.6.0 (2006/09/29) useLabel 属性 追加
332         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
333         *
334         * @param   name         ラジオの name
335         * @param   selectValue  選択されている値
336         * @param   useLabel     ラベル表示の有無 [true:有/false:無]
337         *
338         * @return  オプションタグ
339         */
340//      public String getRadio( final String name,final String selectValue,final boolean useLabel ) {
341//              String errMsg = "このクラスでは実装されていません。";
342//              throw new UnsupportedOperationException( errMsg );
343//      }
344
345        /**
346         * 初期値が選択済みの 選択肢(オプション)を返します。
347         * このオプションは、引数の値を初期値とするオプションタグを返します。
348         * ※ このクラスでは実装されていません。
349         *
350         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
351         *
352         * @param   selectValue  選択されている値
353         *
354         * @return  オプションタグ
355         */
356//      public String getRadioLabel( final String selectValue ) {
357//              String errMsg = "このクラスでは実装されていません。";
358//              throw new UnsupportedOperationException( errMsg );
359//      }
360
361        /**
362         * 選択肢(value)に対するラベルを返します。
363         * 選択肢(value)が、存在しなかった場合は、選択肢そのものを返します。
364         * getValueLabel( XX ) は、getValueLabel( XX,false ) と同じです。
365         *
366         * @og.rev 5.7.7.1 (2014/06/13) Selection_NULL を 継承するため、削除
367         *
368         * @param   selectValue 選択肢の値
369         *
370         * @return  選択肢のラベル
371         * @see     #getValueLabel( String,boolean )
372         */
373//      public String getValueLabel( final String selectValue ) {
374//              return getValueLabel( selectValue,false );
375//      }
376
377        /**
378         * 選択肢(value)に対するラベルを返します。
379         * 選択肢(value)が、存在しなかった場合は、選択肢そのものを返します。
380         * このメソッドでは、短縮ラベルを返すかどうかを指定するフラグを指定します。
381         * getValueLabel( XX,false ) は、getValueLabel( XX ) と同じです。
382         *
383         * @og.rev 4.0.0.0 (2005/11/30) を追加
384         * @og.rev 5.3.5.0 (2011/05/01) 名称(短)表示時に名称(長)をツールチップで表示する。
385         *
386         * @param       selectValue     選択肢の値
387         * @param       flag    短縮ラベルを使用する [true:使用する/false:しない]
388         *
389         * @return  選択肢のラベル
390         * @see     #getValueLabel( String )
391         */
392        @Override
393        public String getValueLabel( final String selectValue,final boolean flag ) {
394                // マッチするアドレスを探す。
395                Integer sel = map.get( selectValue );
396
397                if( sel == null ) {
398                        return selectValue;
399                }
400                else {
401                        if( isShortLavel && flag ) {
402                                return "<span title=\""+label[sel.intValue()]+"\">"+slabel[sel.intValue()]+"</span>";
403                        }
404                        else {
405                                return label[sel.intValue()] ;
406                        }
407                }
408        }
409
410        /**
411         * マルチ・キーセレクトを使用するかどうかを返します。
412         * true:使用する。false:使用しない です。
413         * ただし、実際に使用するかどうかは、HTML出力時に決めることが出来ます。
414         * ここでは、USE_MULTI_KEY_SELECT が true で、USE_SIZE(=20)以上の場合に
415         * true を返します。
416         *
417         * @og.rev 3.5.5.7 (2004/05/10) 新規作成
418         *
419         * @return  選択リストで、マルチ・キーセレクトを使用するかどうか(true:使用する)
420         */
421        @Override
422        public boolean useMultiSelect() {
423                return isMultiSelect;
424        }
425
426        /**
427         * オブジェクトのキャッシュが時間切れかどうかを返します。
428         * キャッシュが時間切れ(無効)であれば、true を、有効であれば、
429         * false を返します。
430         *
431         * @og.rev 4.0.0.0 (2005/01/31) 新規作成
432         *
433         * @return  キャッシュが時間切れなら true
434         */
435        @Override
436        public boolean isTimeOver() {
437                return ( System.currentTimeMillis() - createTime ) > DB_CACHE_TIME ;
438        }
439}