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.plugin.table; 017 018import java.util.Map; 019import java.util.HashMap; 020 021import java.util.List; // 8.0.0.1 (2021/10/08) 022import java.util.ArrayList; // 8.0.0.1 (2021/10/08) 023 024import org.opengion.fukurou.util.StringUtil; 025import org.opengion.fukurou.util.HybsDateUtil; // 8.0.1.2 (2021/11/19) 026// import org.opengion.hayabusa.common.HybsSystem; // 8.0.0.0 (2021/09/30) 027import org.opengion.hayabusa.db.AbstractTableFilter; 028import org.opengion.hayabusa.db.DBColumn; 029import org.opengion.hayabusa.db.DBColumnConfig; 030import org.opengion.hayabusa.db.DBTableModel; 031import org.opengion.hayabusa.db.DBTableModelUtil; 032import org.opengion.hayabusa.resource.ResourceManager; 033import org.opengion.hayabusa.html.ViewMarker; // 8.0.0.0 (2021/09/30) 034 035/** 036 * TableFilter_ROTATE は、TableFilter インターフェースを継承した、DBTableModel 処理用の 037 * 実装クラスです。 038 * 039 * ここではテーブルの回転<del>及びその逆回転</del>を行います。 040 * 8.0.0.0 (2021/07/31) 逆回転 廃止 041 * 042 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。 043 * 【パラメータ】 044 * { 045 * KEY_CLM : キーカラム(複数指定可) (必須) 046 * ROTATE_CLM : 回転するカラム (必須) 047 * ROTATE_LBL : 回転カラムのヘッダーラベル (任意指定) 8.0.0.1 (2021/10/08) 048 * ADD_CLMS : 回転するカラム値を外部から与えます (任意指定) 8.0.1.2 (2021/11/19) 049 * LBL_FORMAT : ヘッダーラベルの日付フォーマット指定(日付) (任意指定) 8.0.1.2 (2021/11/19) 050 * VALUE_CLM : 回転カラムの値 (必須) 051 * USE_LABEL : 値ラベルのカラムを生成するか (任意指定 初期値:false) 8.0.0.0 (2021/07/31) 052 * USE_RENDERER : 値の表示に、renndererを使用するか (任意指定 初期値:false) 8.0.0.0 (2021/07/31) 053 * USE_MARKER : 値の表示に、viewMarkerを使用するか (任意指定 初期値:false) 8.0.0.0 (2021/09/30) 054 * <del>REVERSE : 回転(false)・逆回転(true) (任意指定 初期値:false)</del> 8.0.0.0 (2021/07/31) 廃止 055 * <del>MUST_CLM : 必須属性を定義するカラム (任意指定 初期値:false)</del> 8.4.1.0 (2023/02/10) Delete 056 * <del>DEF_CLM : 初期値を定義するカラム (任意指定)</del> 8.4.1.0 (2023/02/10) Delete 057 * DEF_VAL : 回転するカラムの値がセットされていないときの初期値(任意指定) 8.4.1.0 (2023/02/10) Add 058 * } 059 * 060 * ※ それぞれに指定されたカラム名が存在しない場合は、処理されませんのでご注意下さい。 061 * 062 * ①回転 063 * キーカラム(KEY_CLM)に指定された値が同じN行を1行として回転します。 064 * (キーカラムの値がブレイクしたタイミングで、行を変更します) 065 * このN行に含まれる回転カラムの値がカラム名に、回転カラム値が各カラムの値になります。 066 * キーカラムは、CSV形式で複数指定可能です。 067 * 068 * 生成されたテーブルモデルのカラムは、始めのMカラムがキーカラムに、その後ろのNカラムが 069 * 回転されたカラムになります。 070 * 071 * 生成されたテーブルのカラムオブジェクトは、ROTATE_CLM で指定されたキーで生成されます。 072 * リソースに存在しない場合は、ROTATE_LBL で、ラベルを指定することができます。 073 * 074 * 8.4.1.0 (2023/02/10) Delete ※使い方が不明の為 075 * <del>また、元テーブルにMUST_CLMにより、各カラムの必須属性を定義することが 076 * できます。(MUST属性は、'1'又は'true'の場合に必須になります。)</del> 077 * 078 * 8.0.0.0 (2021/07/31) 079 * USE_LABEL="true" を指定した場合、VALUE_CLM のラベルを、キーカラムと回転カラムの間に 080 * 追加します。これは、VALUE_CLM を 複数指定できる機能追加に伴う処置です。 081 * 082 * 8.0.0.0 (2021/07/31) 逆回転 廃止 083 * <del>②逆回転 084 * 回転時の逆の挙動になります。 085 * "キーカラムに指定されたカラム以外"を回転カラムで指定されたカラムの値として分解します。 086 * 各回転カラムの値は、回転カラム値に指定されたカラムに格納されます。 087 * 088 * 分解後のカラム数は、キーカラム数 + 2 (回転カラム、回転カラム値)になります。 089 * また、行数は、(分解前の行数) x (回転カラム数)になります。 090 *</del> 091 * 092 * @og.formSample 093 * ●形式: 094 * ① <og:tableFilter classId="ROTATE" selectedAll="true" 095 * keys="KEY_CLM,ROTATE_CLM,VALUE_CLM" vals='"GOKI,MAX_SID,MAX_TM_RPS",TOKEN,X_VAL' /> 096 * 097 * ② <og:tableFilter classId="ROTATE" selectedAll="true" > 098 * { 099 * KEY_CLM : GOKI,MAX_SID,MAX_TM_RPS ; 100 * ROTATE_CLM : TOKEN ; 101 * VALUE_CLM : X_VAL ; 102 * } 103 * </og:tableFilter> 104 * 105 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 106 * @og.rev 8.0.0.0 (2021/07/31) VALUE_CLMの複数指定対応と、USE_LABEL属性の追加 107 * @og.rev 8.4.1.0 (2023/02/10) DEF_VAL属性追加、使い方が不明のMUST_CLMとDEF_CLM属性廃止 108 * 109 * @version 0.9.0 2000/10/17 110 * @author Hiroki Nakamura 111 * @since JDK1.1, 112 */ 113public class TableFilter_ROTATE extends AbstractTableFilter { 114 /** このプログラムのVERSION文字列を設定します。 {@value} */ 115 private static final String VERSION = "8.4.1.0 (2023/02/10)" ; 116 117 private DBTableModel table ; // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義 118 private ResourceManager resource ; // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義 119 120 /** 121 * デフォルトコンストラクター 122 * 123 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。 124 */ 125 public TableFilter_ROTATE() { 126 super(); 127 initSet( "KEY_CLM" , "キーカラム(複数指定可)(必須)" ); 128 initSet( "ROTATE_CLM" , "回転するカラム(必須)" ); 129 initSet( "ROTATE_LBL" , "回転カラムのヘッダーラベル" ); // 8.0.0.1 (2021/10/08) 130 initSet( "ADD_CLMS" , "回転するカラム値を外部から与えます" ); // 8.0.1.2 (2021/11/19) 131 initSet( "LBL_FORMAT" , "ヘッダーラベルのフォーマット指定(日付)" ); // 8.0.1.2 (2021/11/19) 132 initSet( "VALUE_CLM" , "回転カラムの値(複数指定可)(必須)" ); 133 initSet( "USE_LABEL" , "値ラベルのカラムを生成するか(初期値:false)" ); // 8.0.0.0 (2021/07/31) 134 initSet( "USE_RENDERER" , "値の表示にrenndererを使用するか(初期値:false)" ); // 8.0.0.0 (2021/07/31) 135 initSet( "USE_MARKER" , "値の表示にviewMarkerを使用するか(初期値:false)" ); // 8.0.0.0 (2021/09/30) 136// initSet( "REVERSE" , "回転(false)/逆回転(true) (初期値:false)" ); // 8.0.0.0 (2021/07/31) 廃止 137// initSet( "MUST_CLM" , "必須属性を定義するカラム (初期値:false)" ); // 8.4.1.0 (2023/02/10) Delete 138// initSet( "DEF_CLM" , "初期値を定義するカラム" ); // 8.4.1.0 (2023/02/10) Delete 139 initSet( "DEF_VAL" , "回転するカラムの値がセットされていないときの初期値"); // 8.4.1.0 (2023/02/10) Add 140 } 141 142 /** 143 * DBTableModel処理を実行します。 144 * 145 * @og.rev 4.3.7.4 (2009/07/01) 新規追加 146 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更 147 * 148 * @return 処理結果のDBTableModel 149 */ 150 public DBTableModel execute() { 151 table = getDBTableModel(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 152 resource = getResource(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 153 154 return getRotateTable(); 155 } 156 157 /** 158 * 回転後のDBTableModelを返します。 159 * 160 * @og.rev 5.1.8.0 (2010/07/01) メソッド名変更(setDefValue ⇒ setDefault) 161 * @og.rev 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 162 * @og.rev 8.0.0.0 (2021/07/31) VALUE_CLMの複数指定対応と、USE_LABEL属性の追加 163 * @og.rev 8.0.0.1 (2021/10/08) ROTATE_LBL(回転カラムのヘッダーラベル)の追加 164 * @og.rev 8.0.1.2 (2021/11/19) ADD_CLMS、LBL_FORMAT 追加 165 * @og.rev 8.4.1.0 (2023/02/10) DEF_VAL属性追加、使い方が不明のMUST_CLMとDEF_CLM属性廃止 166 * 167 * @return 回転後のDBTableModel 168 */ 169 private DBTableModel getRotateTable() { 170 // 8.0.0.0 (2021/07/31) VALUE_CLMの複数指定対応と、USE_LABEL属性の追加 171 // エラー時の原因表示を入れておきます。 172 173 final String tmpKeyClm = getValue( "KEY_CLM" ); 174 final String[] keyClm = StringUtil.csv2Array( tmpKeyClm ); 175 if( keyClm.length == 0 ) { System.out.println( "KEY_CLM is none[" + tmpKeyClm + "]" ); } 176 177 final String tmpRotClm = getValue( "ROTATE_CLM" ); 178 final int rotateNo = table.getColumnNo( tmpRotClm ); // 8.0.0.0 (2021/09/30) エラーにします。 179 180 // 8.0.0.1 (2021/10/08) ROTATE_LBL(回転カラムのヘッダーラベル)の追加 181 final String tmpRotLbl = getValue( "ROTATE_LBL" ); 182 final int lblNo = table.getColumnNo( tmpRotLbl, false ); // 任意なので; 183 184 final String tmpValClm = getValue( "VALUE_CLM" ); 185 final String[] valClm = StringUtil.csv2Array( tmpValClm ); 186 if( valClm.length == 0 ) { System.out.println( "VALUE_CLM is none[" + tmpValClm + "]" ); } 187 188 int clmCount = 0; // 回転後のカラム数 189 // キーカラムのカラム番号を求め、カラム数としてカウントします。 190 final Map<String, Integer> clmMap = new HashMap<>(); 191 final int[] keyNos = new int[keyClm.length]; 192 for( int i=0; i<keyNos.length; i++ ) { 193 keyNos[i] = table.getColumnNo( keyClm[i] ); // 8.0.0.0 (2021/09/30) エラーにします。 194 clmMap.put( keyClm[i], clmCount++ ); // 固定カラム 195 } 196 197 // 8.0.0.0 (2021/07/31) USE_LABEL属性の追加 198 final boolean useValLbl = StringUtil.nval( getValue( "USE_LABEL" ), false ); 199 if( useValLbl ) { 200 clmMap.put( "LABEL", clmCount++ ); // ラベルカラム 201 } 202 203 // ============ 以下は回転カラムの処理 ============ 204 205 final int valStartNo = clmCount ; // 値カラム(回転カラム)の開始アドレス 206 final List<String> lblList = new ArrayList<>(); // 8.0.0.1 (2021/10/08) ROTATE_LBL(未使用時は、nullをセットする) 207 for( int i=0; i<valStartNo; i++ ) { lblList.add( null ); } // 後の処理を共通にするため。 208 209 // 8.0.1.2 (2021/11/19) LBL_FORMAT 回転カラムのラベルフォーマット(日付)を設定します。 210 final String lblFormat = getValue( "LBL_FORMAT" ); // "MM/dd\n(EEE)" などの日付フォーマットを指定します。 211 212 // 8.0.1.2 (2021/11/19) ADD_CLMS 回転カラムを追加します。 213 final String tmpAddClms = getValue( "ADD_CLMS" ); 214 final String[] addClms = StringUtil.csv2Array( tmpAddClms ); 215 for( final String clm : addClms ) { 216 clmMap.put( clm, clmCount++ ); // ラベルカラム 217 if( lblFormat != null ) { 218 lblList.add( HybsDateUtil.getDateFormat( lblFormat,clm ) ); // 後の処理を共通にするため。 219 } 220 } 221 222 // 8.0.0.0 (2021/07/31) USE_RENDERER属性の追加 223 final boolean useValRend = StringUtil.nval( getValue( "USE_RENDERER" ), false ); 224 // 8.0.0.0 (2021/09/30) USE_MARKER属性の追加 225 final boolean useMarker = StringUtil.nval( getValue( "USE_MARKER" ), false ); 226 final ViewMarker viewMarker = useMarker ? getViewMarker() : null; 227 228 // 値カラムのカラム番号を求める。(カラム数としてカウントしない=行が増える) 229 final int[] valNos = new int[valClm.length]; 230 final DBColumn[] valClmns = new DBColumn[valClm.length]; 231 for( int i=0; i<valNos.length; i++ ) { 232 valNos[i] = table.getColumnNo( valClm[i] ); // 8.0.0.0 (2021/09/30) エラーにします。 233 if( useValRend ) { 234 valClmns[i] = table.getDBColumn( valNos[i] ); 235 } 236 } 237 238 int rowCount = 0; // 回転後の行数(KEY_CLMで指定したユニークになる行数で、実際の行数は、VALUE_CLM 倍になる) 239 // 回転カラムの値から回転後のカラム数を求めます。 240 // また同時に、キーカラムの値のブレイク数により行数を求めます。 241 final Map<String, Integer> rowMap = new HashMap<>(); 242 final Map<String, Boolean> mustMap = new HashMap<>(); 243 final Map<String, String> defaultMap = new HashMap<>(); 244// final int mustNo = table.getColumnNo( getValue( "MUST_CLM"), false ); // 任意なので 8.4.1.0 (2023/02/10) Delete 245// final int defNo = table.getColumnNo( getValue( "DEF_CLM" ), false ); // 任意なので 8.4.1.0 (2023/02/10) Delete 246 for( int i=0; i<table.getRowCount(); i++ ) { 247 final String clmKey = table.getValue( i, rotateNo ); 248 if( clmMap.get( clmKey ) == null ) { 249 clmMap.put( clmKey, clmCount++ ); // 回転カラム 250 251 if( lblFormat == null ) { 252 // 8.0.0.1 (2021/10/08) ROTATE_LBL(未使用時は、nullをセットする) 253 lblList.add( lblNo >= 0 ? table.getValue( i, lblNo ) : null ); // 後の処理を共通にするため。 254 } 255 else { 256 // 8.0.1.2 (2021/11/19) LBL_FORMAT 回転カラムのラベルフォーマット(日付)を設定します。 257 final String fmt = lblNo >= 0 ? table.getValue( i, lblNo ) : clmKey ; 258 lblList.add( HybsDateUtil.getDateFormat( lblFormat,fmt ) ); // 後の処理を共通にするため。 259 } 260 } 261 // 必須カラム抜き出し 8.4.1.0 (2023/02/10) Delete 262// if( mustNo > -1 && StringUtil.nval( table.getValue( i, mustNo ), false ) ) { 263// mustMap.put( clmKey, true ); 264// } 265 // デフォルト値を書き換えるカラムの抜き出し 8.4.1.0 (2023/02/10) Delete 266// if( defNo > -1 && table.getValue( i, defNo ) != null && table.getValue( i, defNo ).length() > 0 ) { 267// defaultMap.put( clmKey, table.getValue( i, defNo ) ); 268// } 269 270 final String rowKey = getSeparatedValue( i, keyNos ); 271 // 6.0.0.1 (2014/04/25) These nested if statements could be combined 272 if( rowKey != null && rowKey.length() > 0 && rowMap.get( rowKey ) == null ) { 273 rowMap.put( rowKey, rowCount++ ); 274 } 275 } 276 277 // 回転後のカラム一覧よりDBTableModelを初期化します。 278 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 279 final String names[] = new String[clmMap.size()]; 280 // 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 281 clmMap.forEach( (k,v) -> names[v] = k ); 282 283 final DBTableModel nTable = DBTableModelUtil.newDBTable(); 284 nTable.init( names.length ); 285 for( int i=0; i<names.length; i++ ) { 286 if( mustMap.get( names[i] ) != null ) { 287 table.addMustType( i, "must" ); 288 } 289// DBColumn column = resource.makeDBColumn( names[i] ); 290 DBColumn column = resource.makeDBColumn( names[i],lblList.get(i) ); // 8.0.0.1 (2021/10/08) ヘッダーのラベル。未使用時は null 291 // 8.0.0.0 (2021/07/31) オリジナルが数字タイプだと、エラーになる可能性があるため 292 final DBColumnConfig dbConfig = column.getConfig(); // 固定カラムより値カラムの方が多いので、処理速度は気にしないことにする。 293 if( valStartNo <= i ) { // 値カラム(回転カラム) 294 dbConfig.setRenderer( "LABEL" ); 295 } 296 297// if( defaultMap.get( names[i] ) != null ) { 298// dbConfig.setDefault( defaultMap.get( names[i] ) ); // 5.1.8.0 (2010/07/01) 299// } 300 final String defVal = defaultMap.get( names[i] ) ; 301 if( defVal != null ) { 302 dbConfig.setDefault( defVal ); // 5.1.8.0 (2010/07/01) 303 } 304 column = new DBColumn( dbConfig ); // 8.0.0.0 (2021/07/31) 305 nTable.setDBColumn( i, column ); // 5.1.8.0 (2010/07/01) 306 } 307 308 // 値の一覧を作成し、DBTableModelに値をセットします。 309 if( rowCount > 0 ) { 310 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 311 final String[][] vals = new String[rowCount*valClm.length][names.length]; // 行数は、valClm.length 倍になる 312 for( int i=0; i<table.getRowCount(); i++ ) { 313 final int row = rowMap.get( getSeparatedValue( i, keyNos ) ); 314 final int clm = clmMap.get( table.getValue( i, rotateNo ) ); 315 316 final int nrow = row * valClm.length ; 317 // 8.0.0.0 (2021/07/31) VALUE_CLM が複数存在した場合の処理 318 for( int k=0; k<valNos.length; k++ ) { 319 for( int j=0; j<keyNos.length; j++ ) { 320 String val2 = table.getValue( i, keyNos[j] ); 321 322 if( viewMarker != null ) { 323 val2 = viewMarker.getMarkerString( i,keyNos[j],val2 ); // ViewMarkerを使用 324 } 325 vals[nrow+k][j] = val2; 326 327 // vals[nrow+k][j] = table.getValue( i, keyNos[j] ); 328 } 329 if( useValLbl ) { 330 // 実際は、上のループの最後の j ( = keyNos.length ) 331 vals[nrow+k][keyNos.length] = table.getColumnLabel( valNos[k] ); 332 } 333 334 String val = table.getValue( i, valNos[k] ); 335 if( useValRend ) { val = valClmns[k].getRendererValue( val ); } // Rndererを使用 336 if( viewMarker != null ) { 337 val = viewMarker.getMarkerString( i,valNos[k],val ); // ViewMarkerを使用 338 } 339 vals[nrow+k][clm] = val; 340 } 341 } 342 343 final String defVal = getValue( "DEF_VAL" ); 344 345 for( int i=0; i<vals.length; i++ ) { 346// nTable.addColumnValues( vals[i] ); 347 nTable.addColumnValues( StringUtil.nval( vals[i],defVal) ); // 8.4.1.0 (2023/02/10) Modify 348 } 349 } 350 351 return nTable; 352 } 353 354 /** 355 * 各行のキーとなるキーカラムの値を連結した値を返します。 356 * 357 * @param row 行番号 358 * @param clmNo カラム番号配列(可変長引数) 359 * 360 * @return 各行のキーとなるキーカラムの値を連結した値 361 * @og.rtnNotNull 362 */ 363 private String getSeparatedValue( final int row, final int... clmNo ) { 364 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 365 for( int i=0; i<clmNo.length; i++ ) { 366 final String val = table.getValue( row, clmNo[i] ); 367 if( val != null && val.length() > 0 ) { 368 if( i > 0 ) { 369 buf.append( "__" ); 370 } 371 buf.append( val ); 372 } 373 } 374 return buf.toString(); 375 } 376}