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.html; 017 018import org.opengion.hayabusa.db.DBTableModel; 019import org.opengion.hayabusa.db.DBColumn; 020import org.opengion.fukurou.util.StringUtil; 021import org.opengion.fukurou.util.Attributes; 022import org.opengion.fukurou.model.Formatter; 023import static org.opengion.fukurou.system.HybsConst.BUFFER_LARGE; // 6.1.0.0 (2014/12/26) refactoring 024 025import java.util.concurrent.ConcurrentMap; // 6.4.3.3 (2016/03/04) 026import java.util.concurrent.ConcurrentHashMap; // 6.4.3.1 (2016/02/12) refactoring 027import java.util.List; 028import java.util.ArrayList; 029import java.util.Arrays ; 030 031/** 032 * ViewMarker インターフェース の実装オブジェクトです。 033 * これを共通のスーパークラスとして 各種表示フォーム(例:HTML表示等)に使います。 034 * 035 * このクラスは、setter/getterメソッドのデフォルト実装を提供しています。 036 * 各種表示フォームに対応したサブクラス上で、 create() をオーバーライドして下さい。 037 * 038 * @og.group 画面表示 039 * 040 * @version 4.0 041 * @author Kazuhiko Hasegawa 042 * @since JDK5.0, 043 */ 044public class ViewMarker_MARKER implements ViewMarker { 045 046 private static final int MARK_NULL = -1; // マーカー未設定 047 private static final int MARK_TRUE = 1; // マーカー作成 048 private static final int MARK_FALSE = 0; // マーカー作成せず 049 050 private List<Attributes> markData ; // 4.0.0 (2005/08/31) 051 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 052 private final ConcurrentMap<Integer,Formatter> formMap = new ConcurrentHashMap<>(); // 6.4.3.1 (2016/02/12) 053 private DBTableModel table ; 054 private int[] markCmlNo ; 055 private int[] isMark ; 056 // 3.5.2.0 (2003/10/20) 057 private String[] markKey ; 058 private String[] markLists ; 059 private String[] instrVals ; // 3.8.8.1 (2007/01/06) 060 private int[] markListNo ; 061 private boolean[] useFmtDeco ; // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効) 062 063 /** 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 */ 064 private final ConcurrentMap<Integer,List<Integer>> clmMap = new ConcurrentHashMap<>(); // 6.4.3.1 (2016/02/12) 065 066 /** 067 * デフォルトコンストラクター 068 * 069 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor. 070 */ 071 public ViewMarker_MARKER() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 072 073 /** 074 * 内容をクリア(初期化)します。 075 * 076 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 077 * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加 078 * @og.rev 3.5.6.1 (2004/06/25) formMap属性を追加 079 * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加 080 * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加 081 * @og.rev 6.4.3.1 (2016/02/12) PMD refactoring. HashMap → ConcurrentHashMap に置き換え。 082 * 083 */ 084 @Override // ViewMarker 085 public void clear() { 086 markData = null; // 4.0.0 (2005/08/31) 087 formMap.clear(); // 6.4.3.3 (2016/03/04) 088 table = null; 089 isMark = null; 090 markKey = null; 091 markLists = null; 092 instrVals = null; // 3.8.8.1 (2007/01/06) 093 markListNo = null; 094 clmMap.clear(); // 6.4.3.3 (2016/03/04) 095 useFmtDeco = null; // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効) 096 } 097 098 /** 099 * カラムに対するマーカーアトリビュートをセットします。 100 * 101 * @og.rev 3.1.0.0 (2003/03/20) Hashtable を使用している箇所で、非同期でも構わない箇所を、HashMap に置換え。 102 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 103 * 104 * @param attri アトリビュート 105 */ 106 @Override // ViewMarker 107 public void addAttribute( final Attributes attri ) { 108 if( markData == null ) { markData = new ArrayList<>(); } 109 markData.add( attri ); 110 } 111 112 /** 113 * このマーカーが、初期化されているかどうかを判定します。 114 * 115 * 使用できる状態の場合は、true , 初期化が出来ていない場合は、false を返します。 116 * 117 * @og.rev 6.7.2.0 (2017/01/16) caseKey,caseVal等で未使用のときの対応。 118 * 119 * @return 初期化状況 [true:初期化済み/false:未初期化] 120 */ 121 @Override // ViewMarker 122 public boolean isUsable() { 123// return markData != null && markData.size() > 0 ; // 本当は、ゼロということは無いはず。 124 return markData != null && !markData.isEmpty() ; // 本当は、ゼロということは無いはず。 // 6.9.7.0 (2018/05/14) PMD 125 } 126 127 /** 128 * 内部に DBTableModel をセットします。 129 * 130 * @og.rev 3.1.1.0 (2003/03/28) 同期メソッド(synchronized付き)を非同期に変更する。 131 * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加 132 * @og.rev 3.5.6.1 (2004/06/25) DBTableModel の再設定に対応。 133 * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加 134 * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加 135 * @og.rev 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応する。 136 * @og.rev 6.4.3.4 (2016/03/11) Formatterに新しいコンストラクターを追加する。 137 * @og.rev 6.7.6.0 (2017/03/17) strictCheck 追加。 138 * 139 * @param tbl DBTableModelオブジェクト 140 */ 141 @Override // ViewMarker 142 public void setDBTableModel( final DBTableModel tbl ) { 143 table = tbl; 144 final int count = markData.size(); // 4.0.0 (2005/08/31) 145 146 isMark = new int[count]; 147 markKey = new String[count]; 148 markCmlNo = new int[count]; 149 markLists = new String[count]; 150 instrVals = new String[count]; 151 markListNo = new int[count]; 152 useFmtDeco = new boolean[count]; // 5.6.3.0 (2013/04/01) [$XXXX],[#XXXX]機能を有効にするかどうか(true:有効) 153 154 Arrays.fill( isMark,MARK_FALSE ); // マーカーの表示可否 155 Arrays.fill( markCmlNo,-1 ); // マーカーの可否を判断するカラム番号 156 Arrays.fill( useFmtDeco,false ); // [$XXXX],[#XXXX]機能を無効にする。(互換性のため) 157 158 for( int intKey=0; intKey<count; intKey++ ) { 159 final Attributes attri = markData.get( intKey ); 160 161 final String column = attri.get( "column" ); 162 163 // 6.7.6.0 (2017/03/17) カラムのDBTableModel存在チェック。初期値が true なので、attri に無い場合も、true になる。 164 final String strChk = attri.get( "strictCheck" ); 165 final boolean strictCheck = ! "false".equalsIgnoreCase( strChk ); 166 167 // 6.4.3.1 (2016/02/12) ConcurrentMap 系は、key,val ともに not null 制限です。 168 final int clm = table.getColumnNo( column,strictCheck ); // 6.7.6.0 (2017/03/17) strictCheck で、true の場合は、clm番号が見つからないときは、Exception発生 169 170 if( clm < 0 ) { continue; } // 6.7.6.0 (2017/03/17) 存在しない場合、以下の処理を行わない。= clmMap に、カラムが登録されない。 171 172 // 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応する。 173 // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし 174 clmMap.computeIfAbsent( clm,k -> new ArrayList<>() ).add( intKey ); 175 176 final String body = attri.get( "body" ); 177 final Formatter formatter = new Formatter( table,body ); // 6.4.3.4 (2016/03/11) 178 // 6.4.3.1 (2016/02/12) ConcurrentMap 系は、key,val ともに not null 制限です。 179 formMap.put( intKey, formatter ); 180 181 makeOnMarkFormat( intKey,attri ); 182 183 useFmtDeco[intKey] = "true".equalsIgnoreCase( attri.get( "useFormatDeco" ) ); // 5.6.3.0 (2013/04/01) 184 } 185 } 186 187 /** 188 * 指定の行列に対するマーカー文字列を返します。 189 * この値は、すでにマーカー文字列処理されている為、RendererValue で 190 * 変換する必要はありません。 191 * 引数の value はそのカラムの値として利用されます。この値は、修飾済みの 192 * 値を与えることが可能です。 193 * 194 * @og.rev 3.5.6.1 (2004/06/25) formMap属性を使用します。 195 * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加 196 * @og.rev 5.3.9.0 (2011/09/01) カラム名の先頭に'$'を付加した場合に、URLEncodeされた値を返すように対応 197 * @og.rev 5.6.3.0 (2013/04/01) useFmtDeco属性を追加([$XXXX],[#XXXX]機能を有効にするかどうか) 198 * @og.rev 6.2.4.0 (2015/05/15) useFmtDeco属性とは無関係に、[!XXXX](値)を対応します。 199 * @og.rev 6.8.3.1 (2017/12/01) [$XXXX param] で、RendererValueのパラメータを動的に変更できます。 200 * 201 * @param row 指定の行 202 * @param clm 指定の列 203 * @param value カラムの値(マーカー文字列処理済) 204 * 205 * @return row行、colum列 のマーカー文字列 206 */ 207 @Override // ViewMarker 208 public String getMarkerString( final int row,final int clm,final String value ) { 209 final int intKey = isOnMark(row,clm) ; 210 if( intKey < 0 ) { return value; } 211 212 final Formatter formatter = formMap.get( intKey ); 213 final int[] clmNo = formatter.getClmNos(); 214 final String[] format = formatter.getFormat(); 215 final char[] types = formatter.getType(); 216 217 final StringBuilder buf = new StringBuilder( BUFFER_LARGE ); 218 int j=0; 219 String val ; 220 for( ; j<clmNo.length; j++ ) { 221 // 6.2.4.0 (2015/05/15) [!XXXX](値)を対応 222 if( clm == clmNo[j] && types[j] != '!' ) { 223 val = value; // 一致する場合の valueは、通常レンデラー 224 } 225 else { 226 val = formatter.getValue(row,clmNo[j]); // 生の値 227 } 228 229 // 5.6.3.0 (2013/04/01) useFmtDeco属性を追加(trueの場合は、[$XXXX],[#XXXX]機能を有効にする) 230 if( useFmtDeco[intKey] ) { 231 final DBColumn dbClm = table.getDBColumn( clmNo[j] ); 232 final String prm = formatter.getClmParam(j); // 6.8.3.1 (2017/12/01) 233 if( types[j] == '$' ) { // レンデラー 234 // 6.8.3.1 (2017/12/01) [$XXXX param] で、RendererValueのパラメータを動的に変更できます。 235 val = formatter.getValue(row,clmNo[j]); // ※ 互換性の関係で、useFmtDecoで、'$' のときのみ、生の値を再取得します。 236 val = prm == null || prm.isEmpty() 237 ? dbClm.getRendererValue( row,val ) 238 : dbClm.getRendererValue( row,val,prm ); 239 } 240 else if( types[j] == '#' ) { // ラベル 241 if( prm == null || prm.isEmpty() ) { 242 val = dbClm.getLabel(); 243 } 244 else { 245 // 6.8.3.1 (2017/12/01) [#XXXX param] で、RendererValueのパラメータを動的に変更できます。 246 switch( prm.charAt(0) ) { // 先頭の文字(文字列のswitchでもよい) 247 case 'L': val = dbClm.getLabel(); break; 248 case 'S': val = dbClm.getShortLabel(); break; 249 case 'T': val = dbClm.getLongLabel(); break; 250 case 'D': val = dbClm.getDescription(); break; 251 default : val = dbClm.getLabel(); break; 252 } 253 } 254 } 255 } 256 // false が以前と同じ処理(互換処理)ただし、view などのフォーマット処理とは異なる。 257 else { 258 // 5.3.9.0 (2011/09/01) カラム名の先頭に'$'を付加した場合URLEncodeされた値を返すように対応 259 if( types[j] == '$' ) { 260 val = StringUtil.urlEncode( val ); 261 } 262 } 263 264 buf.append( format[j] ).append( val ); 265 } 266 if( j < format.length ) { buf.append( format[j] ); } 267 String rtn = StringUtil.replace( buf.toString(),"{I}",String.valueOf( row ) ); 268 269 // 3.8.8.1 (2007/01/06) instrVals属性を追加 270 if( instrVals[intKey] != null ) { 271 final String[] vals = StringUtil.csv2Array( instrVals[intKey],' ' ); 272 for( int i=0; i<vals.length; i++ ) { 273 final String css = "<span class=\"instr" + i + "\">" + vals[i] + "</span>"; 274 rtn = StringUtil.replace( rtn,vals[i],css ); 275 } 276 } 277 return rtn ; 278 } 279 280 /** 281 * マーカーを作成する/作成しないの指定カラム番号を求めます。 282 * また、int[列番号] isMark を初期化します。 283 * 284 * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加 285 * @og.rev 3.8.8.1 (2007/01/06) instrVals属性を追加 286 * 287 * @param intKey カラムキーの番号 288 * @param attri アトリビュート 289 */ 290 private void makeOnMarkFormat( final int intKey,final Attributes attri ) { 291 final String onMark = attri.get( "onMark" ); 292 final String markList = attri.get( "markList" ); 293 instrVals[intKey] = attri.get( "instrVals" ); // 3.8.8.1 (2007/01/06) 294 295 // 3.5.6.0 (2004/06/18) nullポインタの参照外しバグの対応 296 // このロジックで値が設定済みであれば、以下の処理は不要である。 297 isMark[intKey] = MARK_NULL; 298 if( onMark == null || onMark.isEmpty() || 299 markList == null || markList.isEmpty() ) { 300 isMark[intKey] = MARK_FALSE; 301 return ; // 3.5.6.0 (2004/06/18) 302 } 303 else if( onMark.charAt(0) != '[' && markList.charAt(0) != '[' ) { 304 isMark[intKey] = markList.indexOf( onMark ) >= 0 ? MARK_TRUE : MARK_FALSE; 305 return ; // 3.5.6.0 (2004/06/18) 306 } 307 308 if( onMark.charAt(0) == '[' ) { 309 markCmlNo[intKey] = table.getColumnNo( onMark.substring( 1,onMark.length()-1 )); 310 } 311 else { 312 markCmlNo[intKey] = -1; 313 markKey[intKey] = onMark ; 314 } 315 316 if( markList.charAt(0) == '[' ) { 317 markListNo[intKey] = table.getColumnNo( markList.substring( 1,markList.length()-1 )); 318 } 319 else { 320 markListNo[intKey] = -1; 321 markLists[intKey] = markList; 322 } 323 } 324 325 /** 326 * マーカーを作成するかどうかを判断します。 327 * int[列番号] isMark には、 未設定 FALSE TRUE の状態を持っており、 328 * 列でマーカーを作成する状態が固定の場合(例えば、onMark属性がデフォルト "true" の場合) 329 * カラムに関係なく、同じ値を返すときに、使用します。 330 * 331 * @og.rev 3.5.2.0 (2003/10/20) markLists,markListNo,markKey属性を追加 332 * @og.rev 3.5.4.0 (2003/11/25) onMark ,markList が null(またはゼロストリング)の場合は、false とする。 333 * @og.rev 4.0.0.0 (2005/08/31) 同一カラムの複数登録を許可します。 334 * @og.rev 6.7.2.0 (2017/01/16) markListの先頭が、"?" の場合、正規表現で判定します。 335 * 336 * @param row 列番号 337 * @param clm カラムキーの名称 338 * 339 * @return 処理するリスト番号、-1 の場合は、該当なし 340 */ 341 private int isOnMark( final int row,final int clm ) { 342 final List<Integer> list = clmMap.get( clm ); 343 if( list == null ) { return -1; } 344 345 for( int i=0; i<list.size(); i++ ) { 346 final int intKey = list.get( i ); 347 if( isMark[intKey] != MARK_NULL ) { 348 if( isMark[intKey] == MARK_TRUE ) { return intKey; } 349 else { continue; } 350 } 351 352 final String onMark ; 353 if( markCmlNo[intKey] < 0 ) { onMark = markKey[intKey] ; } 354 else { onMark = table.getValue( row,markCmlNo[intKey] ); } 355 356 // 3.5.4.0 (2003/11/25) 追加 357 if( onMark == null || onMark.isEmpty() ) { continue; } 358 359 final String markList ; 360 if( markListNo[intKey] < 0 ) { markList = markLists[intKey] ; } 361 else { markList = table.getValue( row,markListNo[intKey] ); } 362 363 // 3.5.4.0 (2003/11/25) 修正 364 if( markList == null || markList.isEmpty() ) { continue; } 365 366 // 6.7.2.0 (2017/01/16) markListの先頭が、"?" の場合、正規表現で判定します。 367 if( markList.charAt(0) == '?' ) { 368 if( onMark.matches( markList.substring(1) ) ) { 369 return intKey; 370 } 371 } 372 else if( markList.indexOf( onMark ) >= 0 ) { return intKey; } 373 } 374 return -1; 375 } 376 377 /** 378 * マーカーされたカラム番号の配列を返します。 379 * 380 * これは特殊処理で、Edit機能で、カラム列をキャッシュしているときに、 381 * JSPのソース等の変更時に、変更が反映されない対応を行う場合、 382 * 通常の ViewFormのサブクラスから、Edit専用の ViewForm_HTMLSeqClmTable で 383 * 制御する場合、ViewMarkerのEditMarkerでは、通常非表示(検索の場合)ですが 384 * Editのポップアップ画面に、表示されてしまうのを避けるため、noDisplay に 385 * 強制的にするカラム番号が必要です。 386 * あくまで、暫定処置です。Edit機能を改修するときに、この機能は削除します。 387 * 388 * ※ この処理は、EditMarkerでのみ有効にします。 389 * 390 * @og.rev 6.0.3.0 (2014/11/13) Edit機能で、JSPソース変更時の対応 391 * 392 * @return マーカーされたカラム番号の配列 393 */ 394 @Override // ViewMarker 395 public int[] getColumnNos() { 396 final int[] rtn = new int[clmMap.size()]; 397 int i=0; 398 for( final Integer obj : clmMap.keySet() ) { 399 rtn[i++] = obj.intValue(); 400 } 401 402 return rtn; 403 } 404}