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 * ここではテーブルの回転、及びその逆回転を行います。 034 * 035 * パラメータは、tableFilterタグの keys, vals にそれぞれ記述するか、BODY 部にCSS形式で記述します。 036 * 【パラメータ】 037 * { 038 * KEY_CLM : キーカラム(複数指定可) (必須) 039 * ROTATE_CLM : 回転するカラム (必須) 040 * VALUE_CLM : 回転カラムの値 (必須) 041 * REVERSE : 回転(false)・逆回転(true) (任意指定 初期値:false) 042 * MUST_CLM : 必須属性を定義するカラム (任意指定 初期値:false) 043 * DEF_CLM : 初期値を定義するカラム (任意指定) 044 * } 045 * 046 * ※ それぞれに指定されたカラム名が存在しない場合は、処理されませんのでご注意下さい。 047 * 048 * ①回転 049 * キーカラムに指定された値が同じN行を1行として回転します。 050 * (キーカラムの値がブレイクしたタイミングで、行を変更します) 051 * このN行に含まれる回転カラムの値がカラム名に、回転カラム値が各カラムの値になります。 052 * キーカラムは、CSV形式で複数指定可能です。 053 * 054 * 生成されたテーブルモデルのカラムは、始めのMカラムがキーカラムに、その後ろのNカラムが 055 * 回転されたカラムになります。 056 * 057 * また、元テーブルにMUST_CLMにより、各カラムの必須属性を定義することが 058 * できます。(MUST属性は、'1'又は'true'の場合に必須になります。) 059 * 060 * ②逆回転 061 * 回転時の逆の挙動になります。 062 * "キーカラムに指定されたカラム以外"を回転カラムで指定されたカラムの値として分解します。 063 * 各回転カラムの値は、回転カラム値に指定されたカラムに格納されます。 064 * 065 * 分解後のカラム数は、キーカラム数 + 2 (回転カラム、回転カラム値)になります。 066 * また、行数は、(分解前の行数) x (回転カラム数)になります。 067 * 068 * @og.formSample 069 * ●形式: 070 * ① <og:tableFilter classId="ROTATE" selectedAll="true" 071 * keys="KEY_CLM,ROTATE_CLM,VALUE_CLM" vals='"GOKI,MAX_SID,MAX_TM_RPS",TOKEN,X_VAL' /> 072 * 073 * ② <og:tableFilter classId="ROTATE" selectedAll="true" > 074 * { 075 * KEY_CLM : GOKI,MAX_SID,MAX_TM_RPS ; 076 * ROTATE_CLM : TOKEN ; 077 * VALUE_CLM : X_VAL ; 078 * } 079 * </og:tableFilter> 080 * 081 * @og.rev 5.6.6.0 (2013/07/05) keys の整合性チェックを追加 082 * 083 * @version 0.9.0 2000/10/17 084 * @author Hiroki Nakamura 085 * @since JDK1.1, 086 */ 087public class TableFilter_ROTATE extends AbstractTableFilter { 088 // * このプログラムのVERSION文字列を設定します。 {@value} */ 089 private static final String VERSION = "6.4.3.4 (2016/03/11)" ; 090 091 private DBTableModel table ; // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義 092 private ResourceManager resource ; // 5.5.2.6 (2012/05/25) 共通に使うため、変数定義 093 094 /** 095 * デフォルトコンストラクター 096 * 097 * @og.rev 6.4.1.1 (2016/01/16) keysMap を、サブクラスから設定させるように変更。 098 */ 099 public TableFilter_ROTATE() { 100 super(); 101 initSet( "KEY_CLM" , "キーカラム(複数指定可)" ); 102 initSet( "ROTATE_CLM" , "回転するカラム" ); 103 initSet( "VALUE_CLM" , "回転カラムの値" ); 104 initSet( "REVERSE" , "回転(false)/逆回転(true) (初期値:false)" ); 105 initSet( "MUST_CLM" , "必須属性を定義するカラム (初期値:false)" ); 106 initSet( "DEF_CLM" , "初期値を定義するカラム" ); 107 } 108 109 /** 110 * DBTableModel処理を実行します。 111 * 112 * @og.rev 4.3.7.4 (2009/07/01) 新規追加 113 * @og.rev 5.5.2.6 (2012/05/25) protected変数を、private化したため、getterメソッドで取得するように変更 114 * 115 * @return 処理結果のDBTableModel 116 */ 117 public DBTableModel execute() { 118 table = getDBTableModel(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 119 resource = getResource(); // 5.5.2.6 (2012/05/25) インターフェースにgetterメソッド追加 120 121 // 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) 122 final boolean reverse = StringUtil.nval( getValue( "REVERSE" ), false ); 123 124 // 逆回転(true) 回転(false) 125 return reverse ? getRecoverdTable() : getRotateTable(); 126 } 127 128 /** 129 * 回転後のDBTableModelを返します。 130 * 131 * @og.rev 5.1.8.0 (2010/07/01) メソッド名変更(setDefValue ⇒ setDefault) 132 * @og.rev 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 133 * 134 * @return 回転後のDBTableModel 135 */ 136 private DBTableModel getRotateTable() { 137 final String[] keyClm = StringUtil.csv2Array( getValue( "KEY_CLM" ) ); 138 final int rotateNo = table.getColumnNo( getValue( "ROTATE_CLM" ), false ); 139 final int valNo = table.getColumnNo( getValue( "VALUE_CLM" ), false ); 140 141 if( keyClm == null || keyClm.length == 0 || rotateNo < 0 || valNo < 0 ) { 142 return table; 143 } 144 145 int clmCount = 0; // 回転後のカラム数 146 // キーカラムのカラム番号を求め、カラム数としてカウントします。 147 final Map<String, Integer> clmMap = new HashMap<>(); 148 int[] keyNos = new int[keyClm.length]; 149 for( int i=0; i<keyNos.length; i++ ) { 150 keyNos[i] = table.getColumnNo( keyClm[i], false ); 151 if( keyNos[i] < 0 ) { 152 return table; 153 } 154 clmMap.put( keyClm[i], clmCount ); 155 clmCount++; 156 } 157 158 int rowCount = 0; // 回転後の行数 159 // 回転カラムの値から回転後のカラム数を求めます。 160 // また同時に、キーカラムの値のブレイク数により行数を求めます。 161 final Map<String, Integer> rowMap = new HashMap<>(); 162 final Map<String, Boolean> mustMap = new HashMap<>(); 163 final Map<String, String> defaultMap = new HashMap<>(); 164 final int mustNo = table.getColumnNo( getValue( "MUST_CLM"), false ); 165 final int defNo = table.getColumnNo( getValue( "DEF_CLM" ), false ); 166 for( int i=0; i<table.getRowCount(); i++ ) { 167 final String clmKey = table.getValue( i, rotateNo ); 168 if( clmMap.get( clmKey ) == null ) { 169 clmMap.put( clmKey, clmCount ); 170 clmCount++; 171 } 172 // 必須カラム抜き出し 173 if( mustNo > -1 && StringUtil.nval( table.getValue( i, mustNo ), false ) ) { 174 mustMap.put( clmKey, true ); 175 } 176 // デフォルト値を書き換えるカラムの抜き出し 177 if( defNo > -1 && table.getValue( i, defNo ) != null && table.getValue( i, defNo ).length() > 0 ) { 178 defaultMap.put( clmKey, table.getValue( i, defNo ) ); 179 } 180 181 final String rowKey = getSeparatedValue( i, keyNos ); 182 // 6.0.0.1 (2014/04/25) These nested if statements could be combined 183 if( rowKey != null && rowKey.length() > 0 && rowMap.get( rowKey ) == null ) { 184 rowMap.put( rowKey, rowCount ); 185 rowCount++; 186 } 187 } 188 189 // 回転後のカラム一覧よりDBTableModelを初期化します。 190 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 191 final String names[] = new String[clmMap.size()]; 192 // 6.4.3.4 (2016/03/11) forループを、forEach メソッドに置き換えます。 193 clmMap.forEach( (k,v) -> names[v] = k ); 194 195 final DBTableModel nTable = DBTableModelUtil.newDBTable(); 196 nTable.init( names.length ); 197 for( int i=0; i<names.length; i++ ) { 198 if( mustMap.get( names[i] ) != null ) { 199 table.addMustType( i, "must" ); 200 } 201 DBColumn column = resource.makeDBColumn( names[i] ); 202 if( defaultMap.get( names[i] ) != null ) { 203 final DBColumnConfig dbConfig = column.getConfig(); 204 dbConfig.setDefault( defaultMap.get( names[i] ) ); // 5.1.8.0 (2010/07/01) 205 column = new DBColumn( dbConfig ); 206 } 207 nTable.setDBColumn( i, column ); // 5.1.8.0 (2010/07/01) 208 } 209 210 // 値の一覧を作成し、DBTableModelに値をセットします。 211 if( rowCount > 0 ) { 212 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 213 final String[][] vals = new String[rowCount][names.length]; 214 for( int i=0; i<table.getRowCount(); i++ ) { 215 final int row = rowMap.get( getSeparatedValue( i, keyNos ) ); 216 final int clm = clmMap.get( table.getValue( i, rotateNo ) ); 217 218 for( int j=0; j<keyNos.length; j++ ) { 219 vals[row][j] = table.getValue( i, keyNos[j] ); 220 } 221 vals[row][clm] = table.getValue( i, valNo ); 222 } 223 for( int i=0; i<vals.length; i++ ) { 224 nTable.addColumnValues( vals[i] ); 225 } 226 } 227 228 return nTable; 229 } 230 231 /** 232 * 各行のキーとなるキーカラムの値を連結した値を返します。 233 * 234 * @param row 行番号 235 * @param clmNo カラム番号配列(可変長引数) 236 * 237 * @return 各行のキーとなるキーカラムの値を連結した値 238 * @og.rtnNotNull 239 */ 240 private String getSeparatedValue( final int row, final int... clmNo ) { 241 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 242 for( int i=0; i<clmNo.length; i++ ) { 243 final String val = table.getValue( row, clmNo[i] ); 244 if( val != null && val.length() > 0 ) { 245 if( i > 0 ) { 246 buf.append( "__" ); 247 } 248 buf.append( val ); 249 } 250 } 251 return buf.toString(); 252 } 253 254 /** 255 * 逆回転後のDBTableModelを返します。 256 * 257 * @return 逆回転後のDBTableModel 258 */ 259 private DBTableModel getRecoverdTable() { 260 final String[] keyClm = StringUtil.csv2Array( getValue( "KEY_CLM" ) ); 261 final String rotateClm = getValue( "ROTATE_CLM" ); 262 final String valClm = getValue( "VALUE_CLM" ); 263 264 if( keyClm == null || keyClm.length == 0 265 || rotateClm == null || rotateClm.isEmpty() 266 || valClm == null || valClm.isEmpty() ) { // 6.1.0.0 (2014/12/26) refactoring 267 return table; 268 } 269 270 // キーカラムのカラム番号を求めます。 271 int[] keyNos = new int[keyClm.length]; 272 for( int i=0; i<keyNos.length; i++ ) { 273 keyNos[i] = table.getColumnNo( keyClm[i], false ); 274 if( keyNos[i] < 0 ) { 275 return table; 276 } 277 } 278 279 // キーカラム以外(回転カラム以外)のカラム番号を求めます。 280 int clmIdx = 0; 281 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 282 final int[] clmNos = new int[table.getColumnCount() - keyNos.length]; 283 for( int i=0; i<table.getColumnCount(); i++ ) { 284 boolean isClm = true; 285 for( int j=0; j<keyNos.length; j++ ) { 286 if( i == keyNos[j] ) { 287 isClm = false; 288 } 289 } 290 if( isClm ) { 291 clmNos[clmIdx] = i; 292 clmIdx++; 293 } 294 } 295 296 // テーブルモデルを初期化します。 297 final DBTableModel nTable = DBTableModelUtil.newDBTable(); 298 nTable.init( keyNos.length + 2 ); 299 for( int i=0; i<keyNos.length; i++ ) { 300 nTable.setDBColumn( i, resource.makeDBColumn( keyClm[i] ) ); 301 } 302 nTable.setDBColumn( keyNos.length, resource.makeDBColumn( rotateClm ) ); 303 nTable.setDBColumn( keyNos.length + 1, resource.makeDBColumn( valClm ) ); 304 305 // 各行を作成し、DBTableModelに登録します。 306 for( int i=0; i<table.getRowCount(); i++ ) { 307 for( int j=0; j<clmNos.length; j++ ) { 308 // 6.3.9.1 (2015/11/27) Found 'DD'-anomaly for variable(PMD) 309 final String[] vals = new String[keyNos.length + 2]; 310 for( int k=0; k<keyNos.length; k++ ) { 311 vals[k] = table.getValue( i, keyNos[k] ); 312 } 313 vals[keyNos.length] = table.getColumnName( clmNos[j] ); 314 vals[keyNos.length + 1] = table.getValue( i, clmNos[j] ); 315 nTable.addColumnValues( vals ); 316 } 317 } 318 319 return nTable; 320 } 321 322}