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