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.system.OgRuntimeException ; // 6.4.2.0 (2016/01/29) 019import org.opengion.fukurou.system.Closer ; 020import org.opengion.fukurou.system.LogWriter; 021import org.opengion.fukurou.util.Argument; 022import org.opengion.fukurou.util.FileUtil; 023import org.opengion.fukurou.util.StringUtil ; 024 025import java.util.Map ; 026import java.util.LinkedHashMap ; 027import java.util.List ; 028import java.util.ArrayList ; 029import java.util.Locale ; // 5.7.3.2 (2014/02/28) ignoreCase が実装されていなかった。 030import java.util.regex.Pattern; // 5.7.3.2 (2014/02/28) regexを利用する場合 031import java.util.regex.Matcher; // 5.7.3.2 (2014/02/28) regexを利用する場合 032 033import java.io.File; 034import java.io.PrintWriter; 035import java.io.BufferedReader; 036import java.io.IOException; 037import java.nio.charset.CharacterCodingException; // 6.3.1.0 (2015/06/28) 038 039/** 040 * Process_GrepChange は、上流から受け取った FileLineModelから、語句を 041 * 置換する、ChainProcess インターフェースの実装クラスです。 042 * 043 * Process_Grep との違いは、チェックするファイルのコピーを(キーワードが存在 044 * しなくとも)作成することと、検索キーに正規表現が使えない、複数行置き換えが 045 * 出来ないことです。 046 * 047 * keywordFile より、置換する語句を含むキーと値のペアー(タブ区切り)を読取り、 048 * 対象とする語句を置換します。 049 * keywordFile に、タブが含まれない行や、先頭にタブが存在している場合は、 050 * その行を読み飛ばします。また、区切りタブは何個存在しても構いません。 051 * 置換文字(値)は、\t の特殊文字が使用できます。 052 * この GrepChange では、語句に、正規表現は使用できません。正規表現のキーワード 053 * や文字列を複数行の文字列と置き換える場合は、Process_Grep を使用してください。 054 * このプログラムでは、上流から受け取った FileLineModel のファイルに対して、 055 * <del>6.3.1.1 (2015/07/10) 置き換えた結果も、同じファイルにセーブします。</del> 056 * 元のファイルを保存したい場合は、予めバックアップを取得しておいてください。 057 * -inEncode は、入力ファイルのエンコード指定になります。 058 * -outEncode は、出力ファイルのエンコードや、キーワードファイルの 059 * エンコード指定になります。(keywordFile は、必ず 出力ファイルと同じエンコードです。) 060 * これらのエンコードが無指定の場合は、System.getProperty("file.encoding") で 061 * 求まる値を使用します。 062 * 063 * 5.7.3.2 (2014/02/28) 064 * -regex=true で、キーワードに正規表現を利用できます。具体的には、String#replaceAll(String,String) 065 * を利用して置換します。 066 * 通常の置換処理は、indexOf で見つけて、StringBuilder#replace(int,int,String) を繰り返して処理しています。 067 * -ignoreCase=true で、検索キーワードに大文字小文字を区別しない処理が可能です。 068 * 069 * 6.3.1.1 (2015/07/10) 070 * ※ 出力ファイルを別フォルダにコピー置換する機能を追加します。 071 * 方法は、Process_FileCopy と同様、inPath と outPath を指定します。 072 * ※ useWordUnit="true" を指定すると、出来るだけ、Grep対象を、単語単位で置換しようとします。 073 * 具体的には、キーワード文字列の前後に、""(ダブルクオート)、''(シングルクオート)、><(タグ記号)、空白、改行を 074 * 付加して、それらを含めてマッチした場合のみ置換する方法を取ります。 075 * 076 * 上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト 077 * である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを 078 * 使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し 079 * できれば、使用可能です。 080 * 081 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。 082 * 引数文字列の 『=』の前後には、スペースは挟めません。必ず、-key=value の様に 083 * 繋げてください。 084 * 085 * Process_GrepChange -keyword=検索文字列 -ignoreCase=true -outfile=OUTFILE -encode=UTF-8 086 * 087 * -keywordFile=キーワード :置換する語句を含むキーと値のペアー(タブ区切り) 088 * [-ignoreCase=[false/true] ] :検索時に大文字小文字を区別しない(true)かどうか(初期値:false[区別する]) 089 * [-regex=[false/true] ] :キーワードに正規表現を利用する(true)かどうか(初期値:false[利用しない]) 090 * [-isChange=置換可否 ] :置換処理を実施する(true)かどうか(初期値:置換する[true]) 091 * [-inPath=入力共通パス ] :上流で検索されたファイルパスの共通部分 092 * [-inEncode=入力エンコード ] :入力ファイルのエンコードタイプ 093 * [-outEncode=出力エンコード ] :出力ファイルやキーワードファイルのエンコードタイプ 094 * [-outPath=出力共通パス ] :出力するファイルパスの共通部分 095 * [-useWordUnit=単語単位置換 ] :出来るだけ、Grep対象を、単語単位で置換しようとします(初期値:false[部分置換]) 096 * [-errAbend=[true/false] ] :異常発生時に、処理を中断(true)するか、継続(false)するかを指定する(初期値:true[中断する]) 097 * [-display=[false/true] ] :結果を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 098 * [-debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 099 * 100 * @version 4.0 101 * @author Kazuhiko Hasegawa 102 * @since JDK5.0, 103 */ 104public class Process_GrepChange extends AbstractProcess implements ChainProcess { 105 // 6.3.1.1 (2015/07/10) useWordUnit="true" 時に、使用することになります。 106 private static final String IN_PTN = "([\"'><\\\t\\\n ])"; // 6.3.1.1 (2015/07/10) ほとんど同じなので、共有する。 107 108 private String[] keyword ; 109 private String[] change ; 110 private Pattern[] pattern ; // 5.7.3.2 (2014/02/28) キーワードに正規表現を利用する場合 111 private boolean ignoreCase ; 112 private boolean regex ; // 5.7.3.2 (2014/02/28) キーワードに正規表現を利用するかどうか 113 private boolean isChange = true; // 5.1.2.0 (2010/01/01) 置換するかどうかを指定可能にする 114 private String inEncode ; 115 private String outPath ; // 6.3.1.1 (2015/07/10) 116 private String outEncode ; 117 private boolean useWordUnit ; // 6.3.1.1 (2015/07/10) 部分置換 118 private boolean errAbend = true; // 6.3.1.1 (2015/07/10) 中断する 119 private boolean display ; // false:表示しない 120 private boolean debug ; // 5.7.3.0 (2014/02/07) デバッグ情報 121 122 private int inPathLen ; // 6.3.1.1 (2015/07/10) 123 private boolean isEquals ; // 6.3.1.1 (2015/07/10) 124 125 private int inCount ; 126 private int findCount ; 127 private int cngCount ; 128 129 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 130 private static final Map<String,String> MUST_PROPARTY ; // [プロパティ]必須チェック用 Map 131 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 132 private static final Map<String,String> USABLE_PROPARTY ; // [プロパティ]整合性チェック Map 133 134 static { 135 MUST_PROPARTY = new LinkedHashMap<>(); 136 MUST_PROPARTY.put( "keywordFile", "置換する語句を含むキーと値のペアー(タブ区切り)(必須)" ); 137 138 USABLE_PROPARTY = new LinkedHashMap<>(); 139 USABLE_PROPARTY.put( "ignoreCase", "検索時に大文字小文字を区別しない(true)かどうか。" + 140 CR + "(初期値:区別する[false])" ); 141 USABLE_PROPARTY.put( "regex", "キーワードに正規表現を利用する(true)かどうか。" + 142 CR + "(初期値:利用しない[false])" ); // 5.7.3.2 (2014/02/28) 143 USABLE_PROPARTY.put( "isChange", "置換処理を実施する(true)かどうか" + 144 CR + "(初期値:置換する[true])" ); 145 USABLE_PROPARTY.put( "inPath", "入力するファイルパスの共通部分" ); // 6.3.1.1 (2015/07/10) 146 USABLE_PROPARTY.put( "inEncode", "入力ファイルのエンコードタイプ" ); 147 USABLE_PROPARTY.put( "outPath", "出力するファイルパスの共通部分" ); // 6.3.1.1 (2015/07/10) 148 USABLE_PROPARTY.put( "outEncode", "出力ファイルやキーワードファイルのエンコードタイプ" ); 149 USABLE_PROPARTY.put( "useWordUnit", "出来るだけ、Grep対象を、単語単位で置換" + 150 CR + "(初期値:false:部分置換)" ); // 6.3.1.1 (2015/07/10) 151 USABLE_PROPARTY.put( "errAbend", "異常発生時に、処理を中断(true)するか、継続(false)するか" + 152 CR + "(初期値:true:中断する)" ); // 6.3.1.0 (2015/06/28) 153 USABLE_PROPARTY.put( "display", "結果を標準出力に表示する(true)かしない(false)か" + 154 CR + "(初期値:false:表示しない)" ); 155 USABLE_PROPARTY.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 156 CR + "(初期値:false:表示しない)" ); // 5.7.3.0 (2014/02/07) デバッグ情報 157 } 158 159 /** 160 * デフォルトコンストラクター。 161 * このクラスは、動的作成されます。デフォルトコンストラクターで、 162 * super クラスに対して、必要な初期化を行っておきます。 163 * 164 */ 165 public Process_GrepChange() { 166 super( "org.opengion.fukurou.process.Process_GrepChange",MUST_PROPARTY,USABLE_PROPARTY ); 167 } 168 169 /** 170 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 171 * 初期処理(ファイルオープン、DBオープン等)に使用します。 172 * 173 * @og.rev 5.1.2.0 (2010/01/01) 置換するかどうかを指定可能にする(isChange)属性追加 174 * @og.rev 5.7.3.2 (2014/02/28) debug の表示と、キーワードの \t の使用、trim() 廃止、ignoreCase の実装、regex の追加 175 * @og.rev 6.3.1.1 (2015/07/10) 出力ファイルを別フォルダにコピー置換する機能を追加 176 * @og.rev 6.3.1.1 (2015/07/10) useWordUnit="true" 時は、出来るだけ、Grep対象を、単語単位で置換しようとします 177 * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 178 * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。 179 * 180 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 181 */ 182 public void init( final ParamProcess paramProcess ) { 183 final Argument arg = getArgument(); 184 185 final String keywordFile = arg.getProparty( "keywordFile" ); 186 ignoreCase = arg.getProparty( "ignoreCase" , ignoreCase ); 187 regex = arg.getProparty( "regex" , regex ); // 5.7.3.2 (2014/02/28) 188 isChange = arg.getProparty( "isChange" , isChange ); // 5.1.2.0 (2010/01/01) 189 inEncode = arg.getProparty( "inEncode" , System.getProperty("file.encoding")); 190 outPath = arg.getProparty( "outPath" , null ); // 6.3.1.1 (2015/07/10) 191 outEncode = arg.getProparty( "outEncode" , System.getProperty("file.encoding")); 192 useWordUnit = arg.getProparty( "useWordUnit", useWordUnit ); // 6.3.1.1 (2015/07/10) 193 errAbend = arg.getProparty( "errAbend" , errAbend ); // 6.3.1.1 (2015/07/10) 194 display = arg.getProparty( "display" , display ); 195 debug = arg.getProparty( "debug" , debug ); // 5.7.3.0 (2014/02/07) デバッグ情報 196 197 // 6.3.1.1 (2015/07/10) 入力と出力が同じか? 198 final String inPath = arg.getProparty( "inPath" , null ); // 6.3.4.0 (2015/08/01) 199 isEquals = outPath == null || inPath == null || inPath.equalsIgnoreCase( outPath ); 200 inPathLen = inPath == null ? 0 : inPath.length(); 201 202 // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 203 final List<String> list = FileUtil.getLineList( keywordFile , outEncode ); // 6.4.5.2 (2016/05/06) 204 final int len = list.size(); // 6.4.5.2 (2016/05/06) 205 if( len == 0 ) { 206 // これは、初期情報取込み処理なので、errAbend 対象外 207 final String errMsg = "keywordFile の内容が 読み取れませんでした。[" + keywordFile + "]" ; 208 throw new OgRuntimeException( errMsg ); 209 } 210 211 println( "keywordFile を、" + len + "件読み取りました。" ); 212 final List<String> keyList = new ArrayList<>( len ); 213 final List<String> cngList = new ArrayList<>( len ); 214 215 for( final String line : list ) { 216 // String line = lines[i].trim(); 217 final int indx = line.indexOf( '\t' ); 218 if( indx <= 0 ) { continue ; } // TAB が先頭や、存在しない行は読み飛ばす。 219 // 5.7.3.2 (2014/02/28) debug の表示と、キーワードの \t の使用、trim() 廃止 220 String key = line.substring( 0,indx ); 221 String cng = line.substring( indx+1 ); 222 223 if( ignoreCase ) { key = key.toUpperCase(Locale.JAPAN); } // 5.7.3.2 (2014/02/28) ignoreCase の実装漏れ 224 225 if( debug ) { println( "[" + key + "]⇒[" + cng + "]" ); } 226 227 key = StringUtil.replace( key,"\\t","\t" ); 228 cng = StringUtil.replace( cng,"\\t","\t" ); 229 230 keyList.add( key ); 231 cngList.add( cng ); 232 } 233 keyword = keyList.toArray( new String[keyList.size()] ); 234 change = cngList.toArray( new String[cngList.size()] ); 235 236 // 5.7.3.2 (2014/02/28) regex=true の場合の処理 237 if( regex ) { 238 pattern = new Pattern[keyword.length]; 239 for( int i=0; i<keyword.length; i++ ) { 240 // 6.3.1.1 (2015/07/10) useWordUnit="true" 時は、出来るだけ、Grep対象を、単語単位で置換しようとします 241 final String keywd = useWordUnit ? IN_PTN + keyword[i] + IN_PTN : keyword[i] ; 242 pattern[i] = ignoreCase ? Pattern.compile( keywd , Pattern.CASE_INSENSITIVE ) 243 : Pattern.compile( keywd ) ; 244 } 245 } 246 } 247 248 /** 249 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 250 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 251 * 252 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 253 */ 254 public void end( final boolean isOK ) { 255 // ここでは処理を行いません。 256 } 257 258 /** 259 * 引数の LineModel を処理するメソッドです。 260 * 変換処理後の LineModel を返します。 261 * 後続処理を行わない場合(データのフィルタリングを行う場合)は、 262 * null データを返します。つまり、null データは、後続処理を行わない 263 * フラグの代わりにも使用しています。 264 * なお、変換処理後の LineModel と、オリジナルの LineModel が、 265 * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。 266 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、 267 * 各処理ごとに自分でコピー(クローン)して下さい。 268 * 269 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。 270 * @og.rev 5.1.2.0 (2010/01/01) 置換するかどうかを指定可能にする(isChange)属性追加 271 * @og.rev 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 272 * @og.rev 5.7.3.2 (2014/02/28) debug の表示と、ignoreCase の実装 273 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 274 * @og.rev 6.3.1.1 (2015/07/10) 出力ファイルを別フォルダにコピー置換する機能を追加 275 * @og.rev 6.3.1.1 (2015/07/10) useWordUnit="true" 時は、出来るだけ、Grep対象を、単語単位で置換しようとします 276 * 277 * @param data オリジナルのLineModel 278 * 279 * @return 処理変換後のLineModel 280 */ 281 @Override // ChainProcess 282 public LineModel action( final LineModel data ) { 283 inCount++ ; 284 final FileLineModel fileData ; 285 if( data instanceof FileLineModel ) { 286 fileData = (FileLineModel)data ; 287 } 288 else { 289 // これは、プログラマーの問題なので、errAbend 対象外 290 final String errMsg = "データが FileLineModel オブジェクトではありません。" + CR ; 291 throw new OgRuntimeException( errMsg ); 292 } 293 294 final File org = fileData.getFile() ; 295 if( ! org.isFile() ) { return data; } 296 297 if( debug ) { println( "File:" + org ); } // 5.1.2.0 (2010/01/01) display の条件変更 298 299 File tempFile = null; 300 PrintWriter tempWrt = null; 301 302 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid declaring a variable if it is unreferenced before a possible exit point. 303 final String orgName = org.getPath(); 304 305 // 5.1.2.0 (2010/01/01) 置換する場合の前処理 306 if( isChange ) { 307 // 6.3.1.1 (2015/07/10) 出力が同じ場合は、従来通り temp出力して、置き換える。 308 if( isEquals ) { 309 tempFile = new File( orgName + "_temp" ); 310 } 311 else { 312 // 入出力が異なる場合 313 tempFile = new File( outPath, org.getAbsolutePath().substring( inPathLen ) ); 314 fileData.setFile( tempFile ); // tempFile は、出力ファイルの事。 315 // 出力先のフォルダが無ければ作成 316 final File parent = tempFile.getParentFile(); 317 if( parent != null && ! parent.exists() && !parent.mkdirs() ) { 318 final String errMsg = "所定のフォルダが作成できませんでした。[" + parent + "]" + CR 319 + " inCount=[" + inCount + "]件" + CR 320 + " data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 321 throwException( errMsg,errAbend ); 322 return null; // ログだけの場合は、以下の処理は打ち切り。 323 } 324 } 325 326 tempWrt = FileUtil.getPrintWriter( tempFile,outEncode ); 327 } 328 329 boolean nextFlag = false; 330 331 // 6.4.1.1 (2016/01/16) PMD refactoring. Avoid instantiating new objects inside loops 332 final StringBuilder buf = new StringBuilder( BUFFER_MIDDLE ); 333 334 final BufferedReader reader = FileUtil.getBufferedReader( org,inEncode ); 335 try { 336 String line ; 337 int lineNo = 0; 338 while((line = reader.readLine()) != null) { 339 lineNo++ ; 340 // 5.7.3.2 (2014/02/28) regex 対応 341 if( regex ) { 342 for( int i=0; i<pattern.length; i++ ) { 343 final Matcher mt = pattern[i].matcher( line ); 344 if( mt.matches() ) { 345 nextFlag = true; // 1度でも見つかれば、true にセット 346 findCount++ ; 347 if( display ) { println( orgName + ":" + lineNo + ":" + keyword[i] + ":" + line ); } 348 if( isChange ) { 349 // 6.3.1.1 (2015/07/10) useWordUnit="true" 時は、出来るだけ、Grep対象を、単語単位で置換しようとします 350 line = mt.replaceAll( "$1" + change[i] + "$2" ); // 前方参照 351 cngCount++ ; 352 } 353 } 354 } 355 } 356 else { 357 final String line2 = ignoreCase ? line.toUpperCase(Locale.JAPAN) : line ; // 6.4.1.1 (2016/01/16) 先に、必要な処理を行う。 358 buf.setLength(0); // new StringBuilder の代わり。 359 buf.append( line ); 360 for( int i=0; i<keyword.length; i++ ) { 361 int indx = line2.indexOf( keyword[i] ); 362 // 置換対象発見。行出力用に見つかれば、true にする。 363 if( indx >= 0 ) { 364 // 6.3.1.1 (2015/07/10) useWordUnit="true" 時は、出来るだけ、Grep対象を、単語単位で置換しようとします 365 // 検索結果の前後の文字が、IN_PTN に含まれている場合のみ、見つかったことにする。 366 if( useWordUnit ) { 367 // 見つかった場所のひとつ前の文字が、IN_PTN に存在しなければ、見つからなかった。 368 if( indx > 0 && IN_PTN.indexOf( line.charAt( indx-1 ) ) < 0 || 369 // 見つかった場所のひとつ後ろの文字が、IN_PTN に存在しなければ、見つからなかった。 370 line.length() < (indx+1) && IN_PTN.indexOf( line.charAt( indx+1 ) ) < 0 ) { 371 // 対象外になったキーワードと行を表示します。 372 if( display ) { println( "NoChange:" + orgName + ":" + lineNo + ":" + keyword[i] + ":" + line ); } 373 continue; 374 } 375 } 376 nextFlag = true; // 1度でも見つかれば、true にセット 377 if( display ) { println( orgName + ":" + lineNo + ":" + keyword[i] + ":" + line ); } 378 findCount++ ; 379 380 // 6.4.1.1 (2016/01/16) 見つかったときだけ、置換処理を実施するように変更。 381 // 置換対象が見つかっても、isChange=true でなければ、置換処理は行わない。 382 if( isChange ) { 383 while( indx >= 0 ) { 384 buf.replace( indx,indx+keyword[i].length(),change[i] ); 385 // 5.7.3.2 (2014/02/28) ignoreCase 対応。 386 final int nxt = indx+change[i].length(); 387 indx = ignoreCase ? buf.toString().toUpperCase(Locale.JAPAN).indexOf( keyword[i],nxt ) 388 : buf.indexOf( keyword[i],nxt ); 389 390 cngCount++ ; 391 } 392 } 393 } 394 } 395 line = buf.toString(); 396 } 397 // 5.1.2.0 (2010/01/01) 置換する場合の処理 398 if( isChange ) { 399 tempWrt.println( line ); // 5.7.3.2 (2014/02/28) regexで出力を共有する為。 400 } 401 } 402 } 403 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 404 catch( final CharacterCodingException ex ) { 405 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 406 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 407 + " [" + org + "] , Encode=[" + inEncode + "]" ; 408 throwException( errMsg,ex,errAbend ); 409 return null; // ログだけの場合は、以下の処理は打ち切り。 410 } 411 catch( final IOException ex ) { 412 final String errMsg = "処理中にエラーが発生しました。" + CR 413 + " [" + org + "] , Encode=[" + inEncode + "]" + CR 414 + " [" + data.getRowNo() + "]件目" + CR 415 + " data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 416 throwException( errMsg,ex,errAbend ); 417 return null; // ログだけの場合は、以下の処理は打ち切り。 418 } 419 finally { 420 Closer.ioClose( reader ); 421 Closer.ioClose( tempWrt ); 422 } 423 424 // 5.1.2.0 (2010/01/01) 置換する場合の処理 425 if( isChange && isEquals ) { // 6.3.1.1 (2015/07/10) 出力が同じ場合の時のみ、後処理が必要。 426 if( nextFlag ) { 427 if( !org.delete() ) { 428 final String errMsg = "所定のファイルを削除できませんでした。[" + org + "]" + CR 429 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 430 throwException( errMsg,errAbend ); 431 } 432 if( !tempFile.renameTo( org ) ) { 433 final String errMsg = "所定のファイルをリネームできませんでした。[" + tempFile + "]" + CR 434 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 435 throwException( errMsg,errAbend ); 436 } 437 } 438 else { 439 if( !tempFile.delete() ) { 440 final String errMsg = "所定のファイルを削除できませんでした。[" + tempFile + "]" + CR 441 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 442 throwException( errMsg,errAbend ); 443 } 444 } 445 } 446 447 return nextFlag ? data : null ; 448 } 449 450 /** 451 * プロセスの処理結果のレポート表現を返します。 452 * 処理プログラム名、入力件数、出力件数などの情報です。 453 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 454 * 形式で出してください。 455 * 456 * @return 処理結果のレポート 457 */ 458 public String report() { 459 // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX' 460 return "[" + getClass().getName() + "]" + CR 461// final String report = "[" + getClass().getName() + "]" + CR 462 + TAB + "Search File Count : " + inCount + CR 463 + TAB + "Key Find Count : " + findCount + CR 464 + TAB + "Key Change Count : " + cngCount ; 465 466// return report ; 467 } 468 469 /** 470 * このクラスの使用方法を返します。 471 * 472 * @return このクラスの使用方法 473 * @og.rtnNotNull 474 */ 475 public String usage() { 476 final StringBuilder buf = new StringBuilder( 1200 ) 477 .append( "Process_GrepChange は、上流から受け取った FileLineModelから、語句を" ).append( CR ) 478 .append( "置換する、ChainProcess インターフェースの実装クラスです。" ).append( CR ) 479 .append( "Process_Grep との違いは、チェックするファイルのコピーを(キーワードが存在" ).append( CR ) 480 .append( "しなくとも)作成することと、検索キーに正規表現が使えない、複数行置き換えが" ).append( CR ) 481 .append( "出来ないことです。" ).append( CR ) 482 .append( CR ) 483 .append( "keywordFile より、置換する語句を含むキーと値のペアー(タブ区切り)を読取り、" ).append( CR ) 484 .append( "対象とする語句を置換します。" ).append( CR ) 485 .append( "keywordFile に、タブが含まれない行や、先頭にタブが存在している場合は、" ).append( CR ) 486 .append( "その行を読み飛ばします。また、区切りタブは何個存在しても構いません。" ).append( CR ) 487 .append( "ただし、タブで区切った前(キー)と後ろ(値)は、trim() されますので、スペース" ).append( CR ) 488 .append( "が前後に存在している場合は、ご注意ください。" ).append( CR ) 489 .append( "置換文字(値)は、\t と \n の特殊文字が使用できます。" ).append( CR ) 490 .append( "この GrepChange では、語句に、正規表現は使用できません。正規表現のキーワード" ).append( CR ) 491 .append( "や文字列を複数行の文字列と置き換える場合は、Process_Grep を使用して下さい。" ).append( CR ) 492 .append( "このプログラムでは、上流から受け取った FileLineModel のファイルに対して、" ).append( CR ) 493 .append( "置き換えた結果も、同じファイルにセーブします。" ).append( CR ) 494 .append( "元のファイルを保存したい場合は、予めバックアップを取得しておいてください。" ).append( CR ) 495 .append( "-inEncode は、入力ファイルのエンコード指定になります。" ).append( CR ) 496 .append( "-outEncode は、出力ファイルのエンコードや、キーワードファイルのエンコード" ).append( CR ) 497 .append( "指定になります。(keywordFile は、必ず 出力ファイルと同じエンコードです。)" ).append( CR ) 498 .append( "これらのエンコードが無指定の場合は、System.getProperty(\"file.encoding\") " ).append( CR ) 499 .append( "で求まる値を使用します。" ).append( CR ) 500 .append( CR ) 501 .append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト" ).append( CR ) 502 .append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを" ).append( CR ) 503 .append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し" ).append( CR ) 504 .append( "できれば、使用可能です。" ).append( CR ) 505 .append( CR ) 506 .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ) 507 .append( "引数文字列の 『=』の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ) 508 .append( "繋げてください。" ).append( CR ) 509 .append( CR ).append( CR ) 510 .append( getArgument().usage() ).append( CR ); 511 512 return buf.toString(); 513 } 514 515 /** 516 * このクラスは、main メソッドから実行できません。 517 * 518 * @param args コマンド引数配列 519 */ 520 public static void main( final String[] args ) { 521 LogWriter.log( new Process_GrepChange().usage() ); 522 } 523}