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