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.fukurou.process; 017 018import org.opengion.fukurou.util.Argument; 019import org.opengion.fukurou.util.HybsEntry ; 020import org.opengion.fukurou.system.LogWriter; 021import org.opengion.fukurou.util.StringUtil; // 5.7.2.3 (2014/01/31) 022 023import java.util.Map ; 024import java.util.LinkedHashMap ; 025 026/** 027 * Process_TableFilter は、上流から受け取ったデータをフィルタする、 028 * ChainProcess インターフェースの実装クラスです。 029 * 030 * 上流(プロセスチェインのデータは上流から下流へと渡されます。)から 031 * 受け取ったLineModel を元に、項目のフィルタリングを行います。 032 * 条件が成立した場合は、下流に流します。複数の条件を指定できますが、 033 * すべて AND で判定されます。 034 * (設定条件すべてを満たす場合のみ、下流にデータを流します。) 035 * 036 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。 037 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に 038 * 繋げてください。 039 * 040 * @og.formSample 041 * Process_TableFilter 042 * 043 * [ -prefix_XXXX=接頭辞 ] :項目名(XXXX)が、指定の接頭辞で始まる場合、条件成立。 044 * [ -suffix_XXXX=接尾辞 ] :項目名(XXXX)が、指定の接尾辞で終わる場合、条件成立。 045 * [ -instr_XXXX=部分文字列 ] :項目名(XXXX)が、指定の部分文字列と一致する場合、条件成立。 046 * [ -equals_XXXX=一致 ] :項目名(XXXX)が、文字列と一致する場合、条件成立。文字列は、大文字小文字は区別しません(equalsIgnoreCase)。 047 * [ -match_XXXX=正規表現 ] :項目名(XXXX)が、正規表現と一致する場合、条件成立。 048 * [ -unmatch_XXXX=正規表現 ] :項目名(XXXX)が、正規表現と一致しない場合、条件成立。 049 * [ -const_XXXX=固定値 ] :-const_FGJ=1 050 * 項目名(XXXX)に、固定値を設定します。 051 * [ -replace_XXXX=固定値 ] :-replace_BIKO="YYYY⇒ZZZZ" (元先指定は、⇒で区切ります。) 052 * 項目名(XXXX)の文字列から、YYYY という文字を ZZZZ に置換します。 053 * -replace_FGJ="_:0 A:1 B:2" (元値:新値 のスペース区切り) 054 * 区切り文字に ⇒ を使用しない場合は、CASE処理を行います。 055 * 項目名(XXXX)の値を、元値 から 新値 に置換します。(完全一致) 056 * [ -display=[false/true] ] :結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 057 * [ -debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 058 * 059 * @version 4.0 060 * @author Kazuhiko Hasegawa 061 * @since JDK5.0, 062 */ 063public class Process_TableFilter extends AbstractProcess implements ChainProcess { 064 /** replace_ で使用する区切り記号 {@value} */ 065 public static final char REP_SEP = '⇒' ; // 4.3.1.1 (2008/08/24) 066 067 private static final String PREFIX_KEY = "prefix_" ; 068 private static final String SUFFIX_KEY = "suffix_" ; 069 private static final String INSTR_KEY = "instr_" ; 070 private static final String EQUALS_KEY = "equals_" ; 071 private static final String MATCH_KEY = "match_" ; 072 private static final String UNMATCH_KEY = "unmatch_"; 073 private static final String CONST_KEY = "const_" ; 074 private static final String REPLACE_KEY = "replace_" ; // 4.3.1.1 (2008/08/24) 075 076 private final LineModelFilter filter = new LineModelFilter(); 077 078 private boolean display ; // 表示しない 079 private boolean debug ; // 5.7.3.0 (2014/02/07) デバッグ情報 080 081 private String[] cnstClm ; // 固定値を設定するカラム名 082 private int[] cnstClmNos ; // 固定値を設定するカラム番号 083 private String[] constVal ; // カラム番号に対応した固定値 084 085 // 4.3.1.1 (2008/08/24) replace 置換関係に必要なデータ 086 private String[] repClm ; // 置換を設定するカラム名 087 private int[] repClmNos ; // 置換を設定するカラム番号 088 private String[] repValFrom ; // カラム番号に対応した置換元文字列 089 private String[] repValTo ; // カラム番号に対応した置換後文字列 090 091 // 5.7.2.3 (2014/01/31) replace 置換の case処理関係に必要なデータ 092 private String[] caseVals ; // カラム番号に対応した置換後文字列 093 094 private boolean firstRow = true; // 最初の一行目 095 private int count ; 096 097 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 098 private static final Map<String,String> MUST_PROPARTY ; // [プロパティ]必須チェック用 Map 099 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 100 private static final Map<String,String> USABLE_PROPARTY ; // [プロパティ]整合性チェック Map 101 102 static { 103 MUST_PROPARTY = new LinkedHashMap<>(); 104 105 USABLE_PROPARTY = new LinkedHashMap<>(); 106 USABLE_PROPARTY.put( PREFIX_KEY , "項目名(XXXX)が、指定の接頭辞で始まる場合、条件成立。" ); 107 USABLE_PROPARTY.put( SUFFIX_KEY , "項目名(XXXX)が、指定の接尾辞で終わる場合、条件成立。" ); 108 USABLE_PROPARTY.put( INSTR_KEY , "項目名(XXXX)が、指定の部分文字列と一致する場合、条件成立。" ); 109 USABLE_PROPARTY.put( EQUALS_KEY , "項目名(XXXX)が、文字列と一致する場合、条件成立。" + 110 CR + "(大文字小文字は区別しない)" ); 111 USABLE_PROPARTY.put( MATCH_KEY , "項目名(XXXX)が、正規表現と一致する場合、条件成立。" ); 112 USABLE_PROPARTY.put( UNMATCH_KEY , "項目名(XXXX)が、正規表現と一致しない場合、条件成立。" ); 113 USABLE_PROPARTY.put( CONST_KEY , "項目名(XXXX)に、固定値を設定します。" ); 114 // 4.3.1.1 (2008/08/24) replace 置換関係 115 USABLE_PROPARTY.put( REPLACE_KEY , "項目名(XXXX)の文字列から、YYYY⇒ZZZZ で部分置換します。" + 116 CR + "項目名(XXXX)の文字列から、_:0 A:1 B:2でCASE置換します。" ); 117 USABLE_PROPARTY.put( "display" , "結果を標準出力に表示する(true)かしない(false)か" + 118 CR + "(初期値:false:表示しない)" ); 119 USABLE_PROPARTY.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 120 CR + "(初期値:false:表示しない)" ); // 5.7.3.0 (2014/02/07) デバッグ情報 121 } 122 123 /** 124 * デフォルトコンストラクター。 125 * このクラスは、動的作成されます。デフォルトコンストラクターで、 126 * super クラスに対して、必要な初期化を行っておきます。 127 * 128 */ 129 public Process_TableFilter() { 130 super( "org.opengion.fukurou.process.Process_TableFilter",MUST_PROPARTY,USABLE_PROPARTY ); 131 } 132 133 /** 134 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 135 * 初期処理(ファイルオープン、DBオープン等)に使用します。 136 * 137 * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応 138 * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。 139 * 140 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 141 */ 142 public void init( final ParamProcess paramProcess ) { 143 final Argument arg = getArgument(); 144 145 display = arg.getProparty( "display",display ); 146 debug = arg.getProparty("debug",debug); // 5.7.3.0 (2014/02/07) デバッグ情報 147 148 HybsEntry[] entry = arg.getEntrys( PREFIX_KEY ); 149 for( int i=0; i<entry.length; i++ ) { 150 filter.add( FilterOperation.PREFIX, entry[i].getKey(), entry[i].getValue() ); 151 } 152 153 entry = arg.getEntrys( SUFFIX_KEY ); 154 for( int i=0; i<entry.length; i++ ) { 155 filter.add( FilterOperation.SUFFIX, entry[i].getKey(), entry[i].getValue() ); 156 } 157 158 entry = arg.getEntrys( INSTR_KEY ); 159 for( int i=0; i<entry.length; i++ ) { 160 filter.add( FilterOperation.INSTR, entry[i].getKey(), entry[i].getValue() ); 161 } 162 163 entry = arg.getEntrys( EQUALS_KEY ); 164 for( int i=0; i<entry.length; i++ ) { 165 filter.add( FilterOperation.EQUALS, entry[i].getKey(), entry[i].getValue() ); 166 } 167 168 entry = arg.getEntrys( MATCH_KEY ); 169 for( int i=0; i<entry.length; i++ ) { 170 filter.add( FilterOperation.MATCH, entry[i].getKey(), entry[i].getValue() ); 171 } 172 173 entry = arg.getEntrys( UNMATCH_KEY ); 174 for( int i=0; i<entry.length; i++ ) { 175 filter.add( FilterOperation.UNMATCH, entry[i].getKey(), entry[i].getValue() ); 176 } 177 178 final HybsEntry[] cnstKey = arg.getEntrys( CONST_KEY ); 179 final int csize = cnstKey.length; 180 cnstClm = new String[csize]; 181 constVal = new String[csize]; 182 for( int i=0; i<csize; i++ ) { 183 cnstClm[i] = cnstKey[i].getKey(); 184 constVal[i] = cnstKey[i].getValue(); 185 } 186 187 // 4.3.1.1 (2008/08/24) replace 置換関係 188 final HybsEntry[] repKey = arg.getEntrys( REPLACE_KEY ); 189 final int rsize = repKey.length; 190 repClm = new String[rsize]; 191 repValFrom = new String[rsize]; 192 repValTo = new String[rsize]; 193 caseVals = new String[rsize]; // 5.7.2.3 (2014/01/31) replace 置換の case処理 194 for( int i=0; i<rsize; i++ ) { 195 repClm[i] = repKey[i].getKey(); 196 final String val = repKey[i].getValue(); // val は、YYYY⇒ZZZZ の形式 197 if( val != null ) { 198 final int ad = val.indexOf( REP_SEP ); // REP_SEP は、'⇒' 199 if( ad >= 0 ) { 200 repValFrom[i] = val.substring( 0,ad ); 201 repValTo[i] = val.substring( ad+1 ); 202 } 203 else { 204 // 5.7.2.3 (2014/01/31) replace 置換の case処理 205 caseVals[i] = val; // 5.7.2.3 (2014/01/31) replace 置換の case処理 206 } 207 } 208 } 209 } 210 211 /** 212 * 引数の LineModel を処理するメソッドです。 213 * 変換処理後の LineModel を返します。 214 * 後続処理を行わない場合(データのフィルタリングを行う場合)は、 215 * null データを返します。つまり、null データは、後続処理を行わない 216 * フラグの代わりにも使用しています。 217 * なお、変換処理後の LineModel と、オリジナルの LineModel が、 218 * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。 219 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、 220 * 各処理ごとに自分でコピー(クローン)して下さい。 221 * 222 * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応 223 * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。 224 * 225 * @param data オリジナルのLineModel 226 * 227 * @return 処理変換後のLineModel 228 */ 229 public LineModel action( final LineModel data ) { 230 count++ ; 231 232 // if( display ) { println( data.dataLine() ); } 233 234 if( !filter.filter( data ) ) { 235 return null; // 不一致 236 } 237 238 if( firstRow ) { 239 final int csize = cnstClm.length; 240 cnstClmNos = new int[csize]; 241 for( int i=0; i<csize; i++ ) { 242 cnstClmNos[i] = data.getColumnNo( cnstClm[i] ); 243 } 244 245 // 4.3.1.1 (2008/08/24) 置換関係対応 246 final int rsize = repClm.length; 247 repClmNos = new int[rsize]; 248 for( int i=0; i<rsize; i++ ) { 249 repClmNos[i] = data.getColumnNo( repClm[i] ); 250 } 251 252 firstRow = false; 253 if( display ) { println( data.nameLine() ); } // 5.7.3.0 (2014/02/07) デバッグ情報 254 } 255 256 if( debug ) { println( "Before:" + data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 257 258 for( int i=0; i<cnstClm.length; i++ ) { 259 data.setValue( cnstClmNos[i],constVal[i] ); 260 } 261 262 // 4.3.1.1 (2008/08/24) 置換関係対応 263 for( int i=0; i<repClm.length; i++ ) { 264 // 5.7.2.3 (2014/01/31) Object が null の時の処理がおかしかったので修正 265 String val = ""; 266 final Object obj = data.getValue( repClmNos[i] ); 267 if( obj != null ) { val = String.valueOf( obj ); } 268 269 if( caseVals[i] == null ) { // 従来からのリプレース処理 270 val = val.replaceAll( repValFrom[i],repValTo[i] ); 271 } 272 else { 273 // 5.7.2.3 (2014/01/31) replace 置換の case処理。 274 val = StringUtil.caseReplace( val , caseVals[i] , false ); 275 } 276 data.setValue( repClmNos[i],val ); 277 } 278 279 if( debug ) { println( "After :" + data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 280 else if( display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 281 return data; 282 } 283 284 /** 285 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 286 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 287 * 288 * @og.rev 4.3.1.1 (2008/08/24) 置換関係対応 289 * @og.rev 5.7.2.3 (2014/01/31) replace 置換の case処理。 290 * 291 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 292 */ 293 public void end( final boolean isOK ) { 294 cnstClm = null; // 固定値を設定するカラム名 295 cnstClmNos = null; // 固定値を設定するカラム番号 296 constVal = null; // カラム番号に対応した固定値 297 298 repClm = null; // 置換を設定するカラム名 299 repClmNos = null; // 置換を設定するカラム番号 300 repValFrom = null; // カラム番号に対応した置換元文字列 301 repValTo = null; // カラム番号に対応した置換後文字列 302 caseVals = null; // 5.7.2.3 (2014/01/31) replace 置換の case処理 303 } 304 305 /** 306 * プロセスの処理結果のレポート表現を返します。 307 * 処理プログラム名、入力件数、出力件数などの情報です。 308 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 309 * 形式で出してください。 310 * 311 * @return 処理結果のレポート 312 */ 313 public String report() { 314 // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX' 315 return "[" + getClass().getName() + "]" + CR 316// final String report = "[" + getClass().getName() + "]" + CR 317 + TAB + "Model Filter : " + filter + CR 318 + TAB + "Output Count : " + count ; 319 320// return report ; 321 } 322 323 /** 324 * このクラスの使用方法を返します。 325 * 326 * @return このクラスの使用方法 327 * @og.rtnNotNull 328 */ 329 public String usage() { 330 final StringBuilder buf = new StringBuilder( BUFFER_LARGE ) 331 .append( "Process_TableFilter は、上流から受け取ったデータをフィルタする、" ).append( CR ) 332 .append( "ChainProcess インターフェースの実装クラスです。" ).append( CR ) 333 .append( CR ) 334 .append( "上流(プロセスチェインのデータは上流から下流へと渡されます。)から" ).append( CR ) 335 .append( "受け取ったLineModel を元に、項目のフィルタリングを行います。" ).append( CR ) 336 .append( "条件が成立した場合は、下流に流します。複数の条件を指定できますが、" ).append( CR ) 337 .append( "すべて AND で判定されます。" ).append( CR ) 338 .append( "(設定条件すべてを満たす場合のみ、下流にデータを流します。)" ).append( CR ) 339 .append( CR ) 340 .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ) 341 .append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ) 342 .append( "繋げてください。" ).append( CR ) 343 .append( CR ).append( CR ) 344 .append( getArgument().usage() ).append( CR ); 345 346 return buf.toString(); 347 } 348 349 /** 350 * このクラスは、main メソッドから実行できません。 351 * 352 * @param args コマンド引数配列 353 */ 354 public static void main( final String[] args ) { 355 LogWriter.log( new Process_TableFilter().usage() ); 356 } 357}