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.FileUtil; 020import org.opengion.fukurou.util.FileString; 021import org.opengion.fukurou.util.Closer ; 022import org.opengion.fukurou.util.StringUtil ; 023import org.opengion.fukurou.util.LogWriter; 024 025import java.util.Arrays; 026import java.util.Map ; 027import java.util.LinkedHashMap ; 028import java.util.regex.Pattern; 029import java.util.regex.Matcher; 030 031import java.io.File; 032import java.io.PrintWriter; 033import java.io.BufferedReader; 034import java.io.IOException; 035 036/** 037 * Process_Grep は、上流から受け取った FileLineModelから、文字列を見つけ出す 038 * ChainProcess インターフェースの実装クラスです。 039 * 040 * 正規表現の keyword を上流から受け取った FileLineModel から検索します。 041 * 見つかった対象ファイルから、指定の文字列を置換する場合は、-change か 042 * -changeFile で、keyword を置換する文字列を指定して下さい。 043 * 置換する文字列には、\t と \n の特殊文字が使用できます。 044 * 045 * 処理対象は、通常は、1行づつ読み取りながら処理を行います。存在チェックの場合は、 046 * 見つかった時点で処理を中止します。これは、該当箇所をピックアップするのではなく、 047 * 存在しているかどうかを判断して、あれば、下流に流すというのが目的だからです。 048 * keyword を、改行を含む正規表現で、検索・置換する場合は、-useBulkRead 属性を 049 * true に設定してください。これは、入力ファイルを一括して読み込みます。 050 * -ignoreCase は、正規表現の検索時にキーの大文字小文字を無視するように指定します。 051 * -notEquals は、結果(見つかればtrue)を反転(見つからなければtrue)します。 052 * これは、行単位ではなく、ファイル単位に判定しますので、change 指定した場合 053 * でも、対象行は、見つかった行です。ただし、下流に対して、見つからない 054 * 場合だけ処理を継続させます。 055 * -inEncode は、入力ファイルのエンコード指定になります。 056 * -outEncode は、出力ファイルのエンコードや、changeFileで指定の置換文字列ファイルの 057 * エンコード指定になります。(changeFile は、必ず 出力ファイルと同じエンコードです。) 058 * これらのエンコードが無指定の場合は、System.getProperty("file.encoding") で 059 * 求まる値を使用します。 060 * -changeFile を使用することで、複数行の文字列に置換することが可能です。 061 * -outfile では、処理を行ったファイル名一覧をセーブします。 062 * 063 * 上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の 064 * ファイルオブジェクトより、指定の文字列が含まれているか検索します。 065 * 上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト 066 * である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを 067 * 使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し 068 * できれば、使用可能です。 069 * 070 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。 071 * 引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に 072 * 繋げてください。 073 * 074 * @og.formSample 075 * Process_Grep -keyword=検索文字列 -ignoreCase=true -outfile=OUTFILE -encode=UTF-8 076 * 077 * -keyword=キーワード :検索する語句 078 * [-ignoreCase=大文字小文字 ] :検索時に大文字小文字を区別しない(true)かどうか(初期値:区別する[false]) 079 * [-notEquals=判定結果の反転] :判定結果を反転させる(true)かどうか(初期値:反転させない[false]) 080 * [-inEncode=入力エンコード ] :入力ファイルのエンコードタイプ 081 * [-outEncode=出力エンコード] :出力ファイルや置換ファイルのエンコードタイプ 082 * [-change=置換文字列 ] :-change="ABCD" \t や \n などの特殊文字が使用できます。 083 * [-changeFile=置換ファイル ] :-changeFile=change.txt このファイルの記述すべてと置換します。 084 * -change と、-changeFile は、同時に指定できません。 085 * 置換機能使用時は、必ず、_backup というファイルが作成されます。 086 * [-insert=[CHANGE/BEFORE/AFTER] ] : 置換でなく挿入する場合の位置を指定します(初期値:CHANGE) 087 * スペースで区切って数字を記述すると、挿入位置にオフセットできます。 088 * [-delete=[false/true] ] : 置換でなく削除します(初期値:false) 089 * [-useBackup=[false/true] ] :trueは、backupファイルを作成します(初期値:false) 090 * [-useBulkRead=[false/true]] :trueは、入力ファイルを一括読込します(初期値:false) 091 * [-display=[false/true] ] :trueは、検索状況を表示します(初期値:false) 092 * [-debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 093 * 094 * @version 4.0 095 * @author Kazuhiko Hasegawa 096 * @since JDK5.0, 097 */ 098public class Process_Grep extends AbstractProcess implements ChainProcess { 099 private static final String[] INSERT_LIST = new String[] { "CHANGE","BEFORE","AFTER" }; 100 101 private Pattern pattern = null; 102 private String keyword = null; 103 private boolean ignoreCase = false; 104 private boolean notEquals = false; 105 private String inEncode = null; 106 private String outEncode = null; 107 private String change = null; 108 private String insert = "CHANGE"; // "CHANGE","BEFORE","AFTER" のどれか 109 private int insOffset = 0; // "BEFORE","AFTER" 時のオフセット 110 private boolean useBackup = false; 111 private boolean useBulkRead = false; // 4.0.1.0 (2007/12/14) 112 private boolean delete = false; 113 private boolean display = false; 114 private boolean debug = false; // 5.1.2.0 (2010/01/01) 115 116 private int inCount = 0; 117 private int findCount = 0; 118 private int cngCount = 0; 119 120 private static final Map<String,String> mustProparty ; // [プロパティ]必須チェック用 Map 121 private static final Map<String,String> usableProparty ; // [プロパティ]整合性チェック Map 122 123 static { 124 mustProparty = new LinkedHashMap<String,String>(); 125 mustProparty.put( "keyword", "検索する語句(必須)" ); 126 127 usableProparty = new LinkedHashMap<String,String>(); 128 usableProparty.put( "ignoreCase", "検索時に大文字小文字を区別しない(true)かどうか。" + 129 CR + "(初期値:区別する[false])" ); 130 usableProparty.put( "notEquals", "検索時に判定結果を反転させる(true)かどうか。" + 131 CR + "(初期値:反転させない[false])" ); 132 usableProparty.put( "inEncode", "入力ファイルのエンコードタイプ" ); 133 usableProparty.put( "outEncode", "出力ファイルや置換ファイルのエンコードタイプ" ); 134 usableProparty.put( "change", "置換文字列 例: -change=\"ABCD\" \\t や \\n などの特殊文字が使用できます。" ); 135 usableProparty.put( "changeFile", "置換文字列ファイル 例: -changeFile=change.txt" + 136 CR + "-change と、-changeFile は、同時に指定できません。" + 137 CR + "置換機能使用時は、必ず、_backup というファイルが作成されます。" ); 138 usableProparty.put( "insert", "[CHANGE/BEFORE/AFTER]:置換でなく挿入する場合の位置を指定します(初期値:CHANGE)" + 139 CR + "スペースで区切って数字を記述すると、挿入位置にオフセットできます。" ); 140 usableProparty.put( "delete", "[false/true]:trueは、置換でなく削除します(初期値:false)" ); 141 usableProparty.put( "useBackup", "[false/true]:trueは、backupファイルを作成します(初期値:false)" ); 142 usableProparty.put( "useBulkRead", "[false/true]:trueは、入力ファイルを一括読込します(初期値:false)" ); 143 usableProparty.put( "display", "[false/true]:trueは、検索状況を表示します(初期値:false)" ); 144 usableProparty.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 145 CR + "(初期値:false:表示しない)" ); 146 } 147 148 /** 149 * デフォルトコンストラクター。 150 * このクラスは、動的作成されます。デフォルトコンストラクターで、 151 * super クラスに対して、必要な初期化を行っておきます。 152 * 153 */ 154 public Process_Grep() { 155 super( "org.opengion.fukurou.process.Process_Grep",mustProparty,usableProparty ); 156 } 157 158 /** 159 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 160 * 初期処理(ファイルオープン、DBオープン等)に使用します。 161 * 162 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 163 */ 164 public void init( final ParamProcess paramProcess ) { 165 Argument arg = getArgument(); 166 167 keyword = arg.getProparty("keyword"); 168 ignoreCase = arg.getProparty("ignoreCase" ,ignoreCase); 169 notEquals = arg.getProparty("notEquals" ,notEquals); 170 inEncode = arg.getProparty("inEncode" ,System.getProperty("file.encoding")); 171 outEncode = arg.getProparty("outEncode" ,System.getProperty("file.encoding")); 172 useBackup = arg.getProparty("useBackup" ,useBackup); 173 useBulkRead = arg.getProparty("useBulkRead" ,useBulkRead); // 4.0.1.0 (2007/12/14) 174 delete = arg.getProparty("delete" ,delete ); 175 insert = arg.getProparty("insert" ,insert ); 176 change = arg.getFileProparty( "change","changeFile",outEncode,false ); 177 display = arg.getProparty("display" ,display); 178 debug = arg.getProparty( "debug" ,debug ); // 5.1.2.0 (2010/01/01) 179 180 if( change != null ) { 181 int adrs = insert.indexOf( ' ' ); // オフセット数字の有無 182 if( adrs > 0 ) { 183 insOffset = Integer.parseInt( insert.substring( adrs+1 ) ); 184 insert = insert.substring( 0,adrs ); 185 } 186 187 boolean isOK = false; 188 for( int i=0; i<INSERT_LIST.length; i++ ) { 189 if( insert.equalsIgnoreCase( INSERT_LIST[i] ) ) { 190 isOK = true; break; 191 } 192 } 193 if( !isOK ) { 194 String errMsg = "insert は、" + Arrays.toString( INSERT_LIST ) 195 + " から指定してください。" + CR 196 + "-insert=[" + insert + "]" ; 197 throw new RuntimeException( errMsg ); 198 } 199 200 change = StringUtil.replace( change,"\\n",CR ); 201 change = StringUtil.replace( change,"\\t","\t" ); 202 } 203 204 if( delete ) { change = ""; } // 削除は、"" 文字列と置換します。 205 206 if( ignoreCase ) { 207 pattern = Pattern.compile( keyword,Pattern.CASE_INSENSITIVE ); 208 } 209 else { 210 pattern = Pattern.compile( keyword ); 211 } 212 } 213 214 /** 215 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 216 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 217 * 218 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 219 */ 220 public void end( final boolean isOK ) { 221 // ここでは処理を行いません。 222 } 223 224 /** 225 * 引数の LineModel を処理するメソッドです。 226 * 変換処理後の LineModel を返します。 227 * 後続処理を行わない場合(データのフィルタリングを行う場合)は、 228 * null データを返します。つまり、null データは、後続処理を行わない 229 * フラグの代わりにも使用しています。 230 * なお、変換処理後の LineModel と、オリジナルの LineModel が、 231 * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。 232 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、 233 * 各処理ごとに自分でコピー(クローン)して下さい。 234 * 235 * @og.rev 4.0.1.0 (2007/12/14) ファイルの一括処理対応。 236 * @og.rev 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 237 * 238 * @param data オリジナルのLineModel 239 * 240 * @return 処理変換後のLineModel 241 */ 242 public LineModel action( final LineModel data ) { 243 inCount++ ; 244 245 final FileLineModel fileData ; 246 if( data instanceof FileLineModel ) { 247 fileData = (FileLineModel)data ; 248 } 249 else { 250 String errMsg = "データが FileLineModel オブジェクトではありません。" + CR ; 251 throw new RuntimeException( errMsg ); 252 } 253 254 File file = fileData.getFile() ; 255 if( ! file.isFile() ) { 256 if( display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 257 return data; 258 } 259 260 final boolean isFind ; 261 try { 262 String fileLine = null; 263 int firstLineNo = -1; 264 if( useBulkRead ) { fileLine = findKeywordAsBulk( file ); } 265 else { firstLineNo = findKeyword( file ); } 266 267 isFind = fileLine != null || firstLineNo >= 0 ; 268 269 // 置換処理 ただし、見つかったときのみ実行 270 if( change != null && isFind ) { 271 // 入力ファイルは、オリジナル_backup ファイルとする。過去のファイルを削除 272 File inFile = new File( file.getPath() + "_backup" ); 273 if( inFile.exists() && !inFile.delete() ) { 274 String errMsg = "過去のBKUPファイルを削除できませんでした。[" + inFile + "]" + CR 275 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 276 throw new RuntimeException( errMsg ); 277 } 278 279 // オリジナルのファイルを、_backup ファイル名に先に変換する。 280 File fromFile = new File( file.getPath() ); 281 if( !fromFile.renameTo( inFile ) ) { 282 String errMsg = "所定のファイルをリネームできませんでした。[" + fromFile + "]" + CR 283 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 284 throw new RuntimeException( errMsg ); 285 } 286 287 // 変換処理 本体 288 if( useBulkRead ) { changeKeywordAsBulk( fileLine,file ); } 289 else { changeKeyword( inFile,file,firstLineNo ); } 290 291 // backup を使わない場合は、削除する。 292 // 4.0.0.0 (2007/11/29) 入れ子if の統合 293 if( ! useBackup && !inFile.delete() ) { 294 String errMsg = "所定のファイルを削除できませんでした。[" + inFile + "]" + CR 295 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 296 throw new RuntimeException( errMsg ); 297 } 298 } 299 } 300 catch ( RuntimeException ex ) { 301 String errMsg = "処理中にエラーが発生しました。[" + data.getRowNo() + "]件目" + CR 302 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 303 throw new RuntimeException( errMsg,ex ); 304 } 305 306 if( display && ( notEquals ^ isFind ) ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 307 return ( notEquals ^ isFind ) ? data : null ; 308 } 309 310 /** 311 * キーワードが存在しているかどうかをチェックします。 312 * ここでは、1行づつ読み取りながら、最初に見つかった時点で制御を返します。 313 * よって、複数行にまたがる keyword でのマッチングは出来ませんが、大きな 314 * ファイル等での検索には、効率的です。 315 * 316 * @og.rev 4.0.1.0 (2007/12/14) 新規追加 317 * 318 * @param file 検索元のファイルオブジェクト 319 * 320 * @return 最初に見つかった行番号(見つからなければ、-1 を返す) 321 */ 322 private int findKeyword( final File file ) { 323 BufferedReader reader = null; 324 325 int firstLineNo = -1; 326 try { 327 reader = FileUtil.getBufferedReader( file,inEncode ); 328 String line ; 329 int lineNo = 0; 330 while((line = reader.readLine()) != null) { 331 lineNo++ ; 332 Matcher mach = pattern.matcher( line ); 333 if( mach.find() ) { 334 if( debug ) { 335 String buf = "DEBUG:\t" + file.getPath() + "(" + lineNo + "): " + line ; 336 println( buf ); 337 } 338 firstLineNo = lineNo; 339 break; 340 } 341 } 342 } 343 catch ( IOException ex ) { 344 String errMsg = "ファイル読取エラーが発生しました。[" + file.getPath() + "]" ; 345 throw new RuntimeException( errMsg,ex ); 346 } 347 finally { 348 Closer.ioClose( reader ); 349 } 350 351 return firstLineNo; 352 } 353 354 /** 355 * キーワードが存在しているかどうかをチェックします。 356 * ここでは、ファイルをすべて読み取ってから、チェックします。 357 * よって、複数行にまたがる keyword でのマッチングが可能です。 358 * 359 * @og.rev 4.0.1.0 (2007/12/14) 新規追加 360 * 361 * @param file 検索元のファイルオブジェクト 362 * 363 * @return 検索元のファイルの文字列化情報(ただし、見つからなければ、null) 364 */ 365 private String findKeywordAsBulk( final File file ) { 366 367 boolean isFind = false; 368 369 FileString sf = new FileString(); 370 sf.setFilename( file.getPath() ); 371 sf.setEncode( inEncode ); 372 String line = sf.getValue(); 373 374 Matcher mach = pattern.matcher( line ); 375 if( mach.find() ) { 376 if( debug ) { println( "DEBUG:\t" + file.getPath() ); } 377 isFind = true; 378 } 379 380 return isFind ? line : null; 381 } 382 383 /** 384 * キーワードを指定の文字列に置き換えます。 385 * useBackup 属性に true を指定した場合、置き換え後の、backup ファイルは、 386 * オリジナル_backup という名称に変わります。 387 * ここでは、1行づつ読み取りながら、変換処理を行います。 388 * よって、複数行にまたがる keyword でのマッチングは出来ませんが、大きな 389 * ファイル等での置換でも、メモリの使用量は抑えられます。 390 * 391 * @og.rev 4.0.1.0 (2007/12/14) 置換処理を独立させます。 392 * 393 * @param inFile 検索元の入力ファイルオブジェクト 394 * @param outFile 変換後の出力ファイルオブジェクト 395 * @param firstLineNo キーワードが存在した場合の最初の行番号 396 */ 397 private void changeKeyword( final File inFile,final File outFile,final int firstLineNo ) { 398 399 BufferedReader reader = FileUtil.getBufferedReader( inFile,inEncode ); 400 PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ); 401 402 String line = null; 403 try { 404 int lineNo = 0; 405 while((line = reader.readLine()) != null) { 406 lineNo++ ; 407 if( lineNo >= firstLineNo ) { 408 Matcher mach = pattern.matcher( line ); 409 410 String chnStr = null; 411 if( "CHANGE".equals( insert ) ) { 412 chnStr = strChange( mach ); 413 } 414 else if( "BEFORE".equals( insert ) ) { 415 chnStr = strBefore( line,mach ); 416 } 417 else if( "AFTER".equals( insert ) ) { 418 chnStr = strAfter( line,mach ); 419 } 420 421 if( chnStr != null ) { 422 line = chnStr; 423 cngCount++ ; // 変換されれば カウント 424 } 425 } 426 writer.println( line ); // readLine() してるので、最後に改行が必要。 427 } 428 } 429 catch ( IOException ex ) { 430 String errMsg = "処理中にエラーが発生しました。[" + line + "]" ; 431 throw new RuntimeException( errMsg,ex ); 432 } 433 finally { 434 Closer.ioClose( reader ); 435 Closer.ioClose( writer ); 436 } 437 } 438 /** 439 * キーワードを指定の文字列に置き換えます。 440 * useBackup 属性に true を指定した場合、置き換え後の、backup ファイルは、 441 * オリジナル_backup という名称に変わります。 442 * ここでは、ファイルをすべて読み取ってから、チェックします。 443 * よって、複数行にまたがる keyword でのマッチングが可能です。 444 * 445 * @og.rev 4.0.1.0 (2007/12/14) 置換処理を独立させます。 446 * 447 * @param fileLine 検索元の行文字列 448 * @param outFile 出力ファイルオブジェクト 449 */ 450 private void changeKeywordAsBulk( final String fileLine,final File outFile ) { 451 PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ); 452 453 String line = fileLine ; 454 try { 455 Matcher mach = pattern.matcher( line ); 456 457 String chnStr = null; 458 if( "CHANGE".equals( insert ) ) { 459 chnStr = strChange( mach ); 460 } 461 else if( "BEFORE".equals( insert ) ) { 462 chnStr = strBefore( line,mach ); 463 } 464 else if( "AFTER".equals( insert ) ) { 465 chnStr = strAfter( line,mach ); 466 } 467 468 if( chnStr != null ) { 469 line = chnStr; 470 cngCount++ ; // 変換されれば カウント 471 } 472 473 writer.print( line ); // 注意:改行コードは、不要 474 } 475 catch ( RuntimeException ex ) { 476 String errMsg = "処理中にエラーが発生しました。[" + outFile.getPath() + "]" ; 477 throw new RuntimeException( errMsg,ex ); 478 } 479 finally { 480 Closer.ioClose( writer ); 481 } 482 } 483 484 /** 485 * insert が、"CHANGE" の場合の処理結果を求めます。 486 * 変換しなかった場合は、null を返します。 487 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 488 * 489 * @param mach キーワードの正規表現 490 * 491 * @return 変換結果(対象行で無い場合は、null) 492 */ 493 private String strChange( final Matcher mach ) { 494 String line = null; 495 if( mach.find() ) { 496 line = mach.replaceAll( change ); 497 } 498 return line ; 499 } 500 501 /** 502 * insert が、"BEFORE" の場合の処理結果を求めます。 503 * 変換しなかった場合は、null を返します。 504 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 505 * 506 * @param line 検索行 507 * @param mach キーワードの正規表現 508 * 509 * @return 変換結果(対象行で無い場合は、null) 510 */ 511 private String strBefore( final String line , final Matcher mach ) { 512 boolean isChng = false; 513 StringBuilder buf = new StringBuilder( line.length() ); 514 int indx = 0; 515 while( mach.find() ) { 516 isChng = true; 517 int strt = mach.start() + insOffset; 518 buf.append( line.substring( indx,strt ) ); 519 buf.append( change ); 520 indx = strt; 521 } 522 523 String rtn = null; 524 if( isChng ) { 525 buf.append( line.substring( indx ) ); 526 rtn = buf.toString(); 527 } 528 529 return rtn ; 530 } 531 532 /** 533 * insert が、"AFTER" の場合の処理結果を求めます。 534 * 変換しなかった場合は、null を返します。 535 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 536 * 537 * @param line 検索行 538 * @param mach キーワードの正規表現 539 * 540 * @return 変換結果(対象行で無い場合は、null) 541 */ 542 private String strAfter( final String line , final Matcher mach ) { 543 boolean isChng = false; 544 StringBuilder buf = new StringBuilder( line.length() ); 545 int indx = 0; 546 while( mach.find() ) { 547 isChng = true; 548 int end = mach.end() + insOffset; 549 buf.append( line.substring( indx,end ) ); 550 buf.append( change ); 551 indx = end; 552 } 553 String rtn = null; 554 if( isChng ) { 555 buf.append( line.substring( indx ) ); 556 rtn = buf.toString(); 557 } 558 559 return rtn ; 560 } 561 562 /** 563 * プロセスの処理結果のレポート表現を返します。 564 * 処理プログラム名、入力件数、出力件数などの情報です。 565 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 566 * 形式で出してください。 567 * 568 * @return 処理結果のレポート 569 */ 570 public String report() { 571 if( findCount < cngCount ) { findCount = cngCount; } 572 573 String report = "[" + getClass().getName() + "]" + CR 574 + TAB + "Search Keyword : " + keyword + CR 575 + TAB + "Search File Count : " + inCount + CR 576 + TAB + "Key Find Count : " + findCount + CR 577 + TAB + "Key Change Count : " + cngCount ; 578 579 return report ; 580 } 581 582 /** 583 * このクラスの使用方法を返します。 584 * 585 * @return このクラスの使用方法 586 */ 587 public String usage() { 588 StringBuilder buf = new StringBuilder(); 589 590 buf.append( "Process_Grep は、上流から受け取った FileLineModelから、文字列を見つけ出す" ).append( CR ); 591 buf.append( "ChainProcess インターフェースの実装クラスです。" ).append( CR ); 592 buf.append( CR ); 593 buf.append( "正規表現の keyword を上流から受け取った FileLineModel から検索します。" ).append( CR ); 594 buf.append( "見つかった対象ファイルから、指定の文字列を置換する場合は、-change か" ).append( CR ); 595 buf.append( "-changeFile で、keyword を置換する文字列を指定して下さい。" ).append( CR ); 596 buf.append( "置換する文字列には、\t と \n の特殊文字が使用できます。" ).append( CR ); 597 buf.append( CR ); 598 buf.append( "処理対象は、通常は、1行づつ読み取りながら処理を行います。存在チェックの場合は、" ).append( CR ); 599 buf.append( "見つかった時点で処理を中止します。これは、該当箇所をピックアップするのではなく、" ).append( CR ); 600 buf.append( "存在しているかどうかを判断して、あれば、下流に流すというのが目的だからです。" ).append( CR ); 601 buf.append( "keyword を、改行を含む正規表現で、検索・置換する場合は、-useBulkRead 属性を" ).append( CR ); 602 buf.append( "true に設定してください。これは、入力ファイルを一括して読み込みます。" ).append( CR ); 603 buf.append( "-ignoreCase は、検索時にキーの大文字小文字を無視するように指定します。" ).append( CR ); 604 buf.append( "-notEquals は、結果(見つかればtrue)を反転(見つからなければtrue)します。" ).append( CR ); 605 buf.append( "これは、行単位ではなく、ファイル単位に判定しますので、change 指定した場合" ).append( CR ); 606 buf.append( "でも、対象行は、見つかった行です。ただし、下流に対して、見つからない" ).append( CR ); 607 buf.append( "場合だけ処理を継続させます。" ).append( CR ); 608 buf.append( "-inEncode は、入力ファイルのエンコード指定になります。" ).append( CR ); 609 buf.append( "-outEncode は、出力ファイルのエンコードや、changeFileで指定の置換文字列" ).append( CR ); 610 buf.append( "ファイルのエンコード指定になります。(changeFile は、必ず 出力ファイルと)" ).append( CR ); 611 buf.append( "同じエンコードです。" ).append( CR ); 612 buf.append( "これらのエンコードが無指定の場合は、System.getProperty(\"file.encoding\") " ).append( CR ); 613 buf.append( "で求まる値を使用します。" ).append( CR ); 614 buf.append( "-changeFile を使用することで、複数行の文字列に置換することが可能です。" ).append( CR ); 615 buf.append( CR ); 616 buf.append( "上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の" ).append( CR ); 617 buf.append( "ファイルオブジェクトより、指定の文字列が含まれているか検索します。" ).append( CR ); 618 buf.append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト" ).append( CR ); 619 buf.append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを" ).append( CR ); 620 buf.append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し" ).append( CR ); 621 buf.append( "できれば、使用可能です。" ).append( CR ); 622 buf.append( CR ); 623 buf.append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ); 624 buf.append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ); 625 buf.append( "繋げてください。" ).append( CR ); 626 buf.append( CR ).append( CR ); 627 628 buf.append( getArgument().usage() ).append( CR ); 629 630 return buf.toString(); 631 } 632 633 /** 634 * このクラスは、main メソッドから実行できません。 635 * 636 * @param args コマンド引数配列 637 */ 638 public static void main( final String[] args ) { 639 LogWriter.log( new Process_Grep().usage() ); 640 } 641}