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 java.util.Arrays; 019import java.util.Enumeration; // 8.1.3.0 (2022/06/03) 020import java.util.jar.JarFile; // 8.1.3.0 (2022/06/03) 021import java.util.jar.JarEntry; // 8.1.3.0 (2022/06/03) 022import java.util.LinkedHashMap; 023import java.util.Locale; // 8.1.3.0 (2022/06/03) 024import java.util.Map; 025import java.util.regex.Pattern; 026import java.util.regex.Matcher; 027 028import java.io.File; 029import java.io.PrintWriter; 030import java.io.BufferedReader; 031import java.io.IOException; 032import java.io.InputStreamReader; // 8.1.3.0 (2022/06/03) 033import java.io.InputStream; // 8.1.3.0 (2022/06/03) 034import java.nio.charset.CharacterCodingException; // 6.3.1.0 (2015/06/28) 035 036import org.opengion.fukurou.system.OgRuntimeException; // 6.4.2.0 (2016/01/29) 037import org.opengion.fukurou.system.OgCharacterException; // 6.5.0.1 (2016/10/21) 038import org.opengion.fukurou.system.Closer; 039import org.opengion.fukurou.system.LogWriter; 040import org.opengion.fukurou.util.Argument; 041import org.opengion.fukurou.util.FileUtil; 042import org.opengion.fukurou.util.StringUtil; 043import org.opengion.fukurou.util.CommentLineParser; // 6.3.1.1 (2015/07/10) 044import org.opengion.fukurou.util.FileInfo; // 6.4.0.2 (2015/12/11) 045import org.opengion.hayabusa.common.HybsSystem; // 8.1.3.0 (2022/06/03) 046 047/** 048 * Process_Grep は、上流から受け取った FileLineModelから、文字列を見つけ出す 049 * ChainProcess インターフェースの実装クラスです。 050 * 051 * 正規表現の keyword を上流から受け取った FileLineModel から検索します。 052 * 見つかった対象ファイルから、指定の文字列を置換する場合は、-change か 053 * -changeFile で、keyword を置換する文字列を指定して下さい。 054 * 置換する文字列には、\t と \n の特殊文字が使用できます。 055 * 056 * 処理対象は、通常は、1行づつ読み取りながら処理を行います。存在チェックの場合は、 057 * 見つかった時点で処理を中止します。これは、該当箇所をピックアップするのではなく、 058 * 存在しているかどうかを判断して、あれば、下流に流すというのが目的だからです。 059 * keyword を、改行を含む正規表現で、検索・置換する場合は、-useBulkRead 属性を 060 * true に設定してください。これは、入力ファイルを一括して読み込みます。 061 * -ignoreCase は、検索時にキーの大文字小文字を無視するように指定します。 062 * -notEquals は、結果(見つかればtrue)を反転(見つからなければtrue)します。 063 * これは、行単位ではなく、ファイル単位に判定しますので、change 指定した場合 064 * でも、対象行は、見つかった行です。ただし、下流に対して、見つからない 065 * 場合だけ処理を継続させます。 066 * -inEncode は、入力ファイルのエンコード指定になります。 067 * -outEncode は、出力ファイルのエンコードや、changeFileで指定の置換文字列ファイルの 068 * エンコード指定になります。(changeFile は、必ず 出力ファイルと同じエンコードです。) 069 * これらのエンコードが無指定の場合は、System.getProperty("file.encoding") で 070 * 求まる値を使用します。 071 * -changeFile を使用することで、複数行の文字列に置換することが可能です。 072 * -outfile では、処理を行ったファイル名一覧をセーブします。 073 * 074 * 上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の 075 * ファイルオブジェクトより、指定の文字列が含まれているか検索します。 076 * 上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト 077 * である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを 078 * 使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し 079 * できれば、使用可能です。 080 * 081 * ※ 6.3.1.1 (2015/07/10) useOmitCmnt、useAllFind 機能追加 082 * 083 * 引数文字列中に空白を含む場合は、ダブルコーテーション("") で括って下さい。 084 * 引数文字列の 『=』 の前後には、空白は挟めません。必ず、-key=value の様に 085 * 繋げてください。 086 * 087 * ※ 8.1.3.0 (2022/06/03) jarPrefix、jarSuffix、jarInstr、useRegexp、saveFile 追加 088 * jar ファイルの中身も検索します。その際、jarファイルに圧縮されているファイル名での 089 * 絞り込みができるように、指定できる属性を追加します。 090 * ただし、jarファイル内の検索は、useAllFind=true(置換ではなく検索だけ最後まで行う)のみです。 091 * 上流から jar ファイルが指定された場合は、常に検索対象になります。 092 * 093 * @og.formSample 094 * Process_Grep -keyword=検索文字列 -ignoreCase=true -outfile=OUTFILE -encode=UTF-8 095 * 096 * -keyword=キーワード :検索する語句 097 * [-ignoreCase=大文字小文字 ] :検索時に大文字小文字を区別しない(true)かどうか(初期値:区別する[false]) 098 * [-notEquals=判定結果の反転] :判定結果を反転させる(true)かどうか(初期値:反転させない[false]) 099 * [-inEncode=入力エンコード ] :入力ファイルのエンコードタイプ 100 * [-outEncode=出力エンコード ] :出力ファイルや置換ファイルのエンコードタイプ 101 * [-change=置換文字列 ] :-change="ABCD" \t や \n などの特殊文字が使用できます。 102 * [-changeFile=置換ファイル ] :-changeFile=change.txt このファイルの記述すべてと置換します。 103 * -change と、-changeFile は、同時に指定できません。 104 * 置換機能使用時は、必ず、_backup というファイルが作成されます。 105 * [-insert=[HEAD/CHANGE/BEFORE/AFTER/TAIL] ] 106 * : 置換でなく挿入する場合の位置を指定します(初期値:CHANGE) 107 * スペースで区切って数字を記述すると、挿入位置にオフセットできます。 108 * [-delete=[false/true] ] : 置換でなく削除します(初期値:false) 109 * [-skipRowCount=スキップ行数 ] : 先頭行から、スキップする行数を指定します(useBulkRead時には使用されません) 110 * [-useBackup=[false/true] ] :trueは、backupファイルを作成します(初期値:false) 111 * [-useBulkRead=[false/true]] :trueは、入力ファイルを一括読込します(初期値:false) 112 * [-useAllFind=[false/true] ] :置換ではなく検索だけ最後まで行う場合、trueを指定します(初期値:false) 113 * [-useOmitCmnt=[false/true]] :コメント部分を削除したファイルでgrep処理を行うかどうかを指定(初期値:false) 114 * [-errAbend=[true/false] ] :異常発生時に、処理を中断(true)するか、継続(false)するかを指定する(初期値:true[中断する]) 115 * [-jarPrefix=接頭辞 ] :File・・・・,View・・・・,など、指定の接頭辞で始まるjarファイルを中を検索 8.1.3.0 (2022/06/03) 116 * [-jarSuffix=接尾辞 ] :.txt|.java|.jsp.... など、指定の接尾辞で終わるjarファイルを中を検索 8.1.3.0 (2022/06/03) 117 * [-jarInstr=部分文字列 ] :jarファイルを中と一致する部分文字列を指定 8.1.3.0 (2022/06/03) 118 * [-useRegexp=[false/true] ] :trueは、正規表現で検索します(初期値:false) 8.1.3.0 (2022/06/03) 119 * [-saveFile=保存ファイル ] :検索結果を指定ファイルに保存します 8.1.3.0 (2022/06/03) 120 * [-display=[false/true] ] :trueは、検索状況を表示します(初期値:false) 121 * [-debug=[false/true] ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない]) 122 * 123 * @version 4.0 124 * @author Kazuhiko Hasegawa 125 * @since JDK5.0, 126 */ 127public class Process_Grep extends AbstractProcess implements ChainProcess { 128 private static final String[] INSERT_LIST = { "HEAD","CHANGE","BEFORE","AFTER","TAIL" }; // 6.2.4.0 (2015/05/15) 129 private static final String ENCODE = "UTF-8"; // 8.1.3.0 (2022/06/03) 130 private static final String AUTO_ENCODE = "autoEncode"; // 8.1.3.0 (2022/06/03) 131 132 /** 8.1.3.0 (2022/06/03) 拡張子をエンコードに変換するMap */ 133 private static final Map<String,String> EXT2ENC = Map.ofEntries( 134 Map.entry("bat" , "Windows-31J"), Map.entry("ken" , "Windows-31J"), 135 Map.entry("sql" , "Windows-31J"), Map.entry("vbs" , "Windows-31J"), 136 Map.entry("css" , "UTF-8"), Map.entry("html" , "UTF-8"), 137 Map.entry("java" , "UTF-8"), Map.entry("js" , "UTF-8"), 138 Map.entry("jsp" , "UTF-8"), Map.entry("xml" , "UTF-8"), 139 Map.entry("jar" , "UTF-8") // 除外されない為に追記 140 ); 141 142 private Pattern pattern; 143 private String keyword; 144 private boolean ignoreCase; 145 private boolean notEquals; 146 private String inEncode; 147 private String outEncode; 148 private String change; 149 private String insert = "CHANGE"; // "HEAD","CHANGE","BEFORE","AFTER","TAIL" のどれか 150 private int insOffset; // "BEFORE","AFTER" 時のオフセット 151 private boolean useBackup; 152 private boolean useBulkRead; // 4.0.1.0 (2007/12/14) 一括読込 153 private boolean delete; 154 private boolean useAllFind; // 6.3.1.1 (2015/07/10) 最後まで検索 155 private boolean useOmitCmnt; // 6.3.1.1 (2015/07/10) コメント除外 156 private boolean errAbend = true; // 6.3.1.0 (2015/06/28) 中断する 157 private String jarPrefix; // 8.1.3.0 (2022/06/03) 158 private String jarSuffix; // 8.1.3.0 (2022/06/03) 159 private String jarInstr; // 8.1.3.0 (2022/06/03) 160 private boolean useRegexp; // 8.1.3.0 (2022/06/03) 161 private String saveFile; // 8.1.3.0 (2022/06/03) 162 private boolean display; 163 private boolean debug; // 5.1.2.0 (2010/01/01) 164 165 private int inCount; 166 private int findCount; 167 private int cngCount; 168 private int skipRowCount; // 6.2.4.0 (2015/05/15) 行スキップ 169 private PrintWriter outWriter; // 8.1.3.0 (2022/06/03) PrintWriterオブジェクト 170 private final String fileURL = HybsSystem.sys( "FILE_URL" ); // 8.1.3.0 (2022/06/03) ファイルURL 171 172 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 173 private static final Map<String,String> MUST_PROPARTY ; // [プロパティ]必須チェック用 Map 174 /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */ 175 private static final Map<String,String> USABLE_PROPARTY ; // [プロパティ]整合性チェック Map 176 177 static { 178 MUST_PROPARTY = new LinkedHashMap<>(); 179 MUST_PROPARTY.put( "keyword", "検索する語句(必須)" ); 180 181 USABLE_PROPARTY = new LinkedHashMap<>(); 182 USABLE_PROPARTY.put( "ignoreCase", "検索時に大文字小文字を区別しない(true)かどうか。" + 183 CR + "(初期値:区別する[false])" ); 184 USABLE_PROPARTY.put( "notEquals", "検索時に判定結果を反転させる(true)かどうか。" + 185 CR + "(初期値:反転させない[false])" ); 186 USABLE_PROPARTY.put( "inEncode", "入力ファイルのエンコードタイプ" ); 187 USABLE_PROPARTY.put( "outEncode", "出力ファイルや置換ファイルのエンコードタイプ" ); 188 USABLE_PROPARTY.put( "change", "置換文字列 例: -change=\"ABCD\" \\t や \\n などの特殊文字が使用できます。" ); 189 USABLE_PROPARTY.put( "changeFile", "置換文字列ファイル 例: -changeFile=change.txt" + 190 CR + "-change と、-changeFile は、同時に指定できません。" + 191 CR + "置換機能使用時は、必ず、_backup というファイルが作成されます。" ); 192 USABLE_PROPARTY.put( "insert", "[HEAD/CHANGE/BEFORE/AFTER/TAIL]:置換でなく挿入する場合の位置を指定します(初期値:CHANGE)" + 193 CR + "スペースで区切って数字を記述すると、挿入位置にオフセットできます。" ); 194 USABLE_PROPARTY.put( "delete", "[false/true]:trueは、置換でなく削除します(初期値:false)" ); 195 USABLE_PROPARTY.put( "skipRowCount","先頭行から、スキップする行数を指定します。" ); // 6.2.4.0 (2015/05/15) 196 USABLE_PROPARTY.put( "useBackup", "[false/true]:trueは、backupファイルを作成します(初期値:false)" ); 197 USABLE_PROPARTY.put( "useBulkRead", "[false/true]:trueは、入力ファイルを一括読込します(初期値:false)" ); 198 USABLE_PROPARTY.put( "useAllFind", "置換ではなく検索だけ最後まで行う場合、trueを指定します(初期値:false)" ); // 6.3.1.1 (2015/07/10) 199 USABLE_PROPARTY.put( "useOmitCmnt", "コメント部分を削除したファイルでgrep処理を行うかどうかを指定(初期値:false)" ); // 6.3.1.1 (2015/07/10) 200 USABLE_PROPARTY.put( "jarPrefix", "File・・・・,View・・・・,など、指定の接頭辞で始まるjarファイルを中を検索" ); // 8.1.3.0 (2022/06/03) 201 USABLE_PROPARTY.put( "jarSuffix", ".txt|.java|.jsp.... など、指定の接尾辞で終わるjarファイルを中を検索" ); // 8.1.3.0 (2022/06/03) 202 USABLE_PROPARTY.put( "jarInstr", "jarファイルを中と一致する部分文字列を指定" ); // 8.1.3.0 (2022/06/03) 203 USABLE_PROPARTY.put( "useRegexp", "[false/true]:trueは、正規表現で検索します(初期値:false)" ); // 8.1.3.0 (2022/06/03) 204 USABLE_PROPARTY.put( "errAbend", "異常発生時に、処理を中断(true)するか、継続(false)するか" + 205 CR + "(初期値:true:中断する)" ); // 6.3.1.0 (2015/06/28) 206 USABLE_PROPARTY.put( "saveFile", "検索結果を指定ファイルに保存します" ); // 8.1.3.0 (2022/06/03) 207 USABLE_PROPARTY.put( "display", "[false/true]:trueは、検索状況を表示します(初期値:false)" ); 208 USABLE_PROPARTY.put( "debug", "デバッグ情報を標準出力に表示する(true)かしない(false)か" + 209 CR + "(初期値:false:表示しない)" ); 210 } 211 212 /** 213 * デフォルトコンストラクター。 214 * このクラスは、動的作成されます。デフォルトコンストラクターで、 215 * super クラスに対して、必要な初期化を行っておきます。 216 * 217 */ 218 public Process_Grep() { 219 super( "org.opengion.fukurou.process.Process_Grep",MUST_PROPARTY,USABLE_PROPARTY ); 220 } 221 222 /** 223 * プロセスの初期化を行います。初めに一度だけ、呼び出されます。 224 * 初期処理(ファイルオープン、DBオープン等)に使用します。 225 * 226 * @og.rev 6.3.1.0 (2015/06/28) errAbend属性追加。 227 * @og.rev 6.3.1.1 (2015/07/10) useOmitCmnt、useAllFind 機能追加 228 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 229 * 230 * @param paramProcess データベースの接続先情報などを持っているオブジェクト 231 */ 232 public void init( final ParamProcess paramProcess ) { 233 final Argument arg = getArgument(); 234 235 keyword = arg.getProparty( "keyword"); 236 ignoreCase = arg.getProparty( "ignoreCase" ,ignoreCase ); 237 notEquals = arg.getProparty( "notEquals" ,notEquals ); 238 inEncode = arg.getProparty( "inEncode" ,System.getProperty("file.encoding")); 239 outEncode = arg.getProparty( "outEncode" ,System.getProperty("file.encoding")); 240 useBackup = arg.getProparty( "useBackup" ,useBackup ); 241 useBulkRead = arg.getProparty( "useBulkRead",useBulkRead); // 4.0.1.0 (2007/12/14) 242 delete = arg.getProparty( "delete" ,delete ); 243 insert = arg.getProparty( "insert" ,insert ); 244 change = arg.getFileProparty( "change" ,"changeFile",outEncode,false ); 245 skipRowCount = arg.getProparty( "skipRowCount",0 ); // 6.2.4.0 (2015/05/15) 246 useAllFind = arg.getProparty( "useAllFind" ,useAllFind); // 6.3.1.1 (2015/07/10) 247 useOmitCmnt = arg.getProparty( "useOmitCmnt",useOmitCmnt); // 6.3.1.1 (2015/07/10) 248 errAbend = arg.getProparty( "errAbend" ,errAbend ); // 6.3.1.0 (2015/06/28) errAbend属性追加 249 jarPrefix = arg.getProparty( "jarPrefix" ,jarPrefix ); // 8.1.3.0 (2022/06/03) 250 jarSuffix = arg.getProparty( "jarSuffix" ,jarSuffix ); // 8.1.3.0 (2022/06/03) 251 jarInstr = arg.getProparty( "jarInstr" ,jarInstr ); // 8.1.3.0 (2022/06/03) 252 useRegexp = arg.getProparty( "useRegexp" ,useRegexp ); // 8.1.3.0 (2022/06/03) 253 saveFile = arg.getProparty( "saveFile" ,saveFile ); // 8.1.3.0 (2022/06/03) 254 display = arg.getProparty( "display" ,display ); 255 debug = arg.getProparty( "debug" ,debug ); // 5.1.2.0 (2010/01/01) 256 257 if( change != null ) { 258 final int adrs = insert.indexOf( ' ' ); // オフセット数字の有無 259 if( adrs > 0 ) { 260 insOffset = Integer.parseInt( insert.substring( adrs+1 ) ); 261 insert = insert.substring( 0,adrs ); 262 } 263 264 boolean isOK = false; 265 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ForLoopCanBeForeach 266// for( int i=0; i<INSERT_LIST.length; i++ ) { 267// if( insert.equalsIgnoreCase( INSERT_LIST[i] ) ) { 268 for( final String insClm : INSERT_LIST ) { 269 if( insert.equalsIgnoreCase( insClm ) ) { 270 isOK = true; break; 271 } 272 } 273 if( !isOK ) { 274 // 実行時エラーではないので、errAbend 対象外 275 final String errMsg = "insert は、" + Arrays.toString( INSERT_LIST ) 276 + " から指定してください。" + CR 277 + "-insert=[" + insert + "]" ; 278 throw new OgRuntimeException( errMsg ); 279 } 280 281 change = StringUtil.replace( change,"\\n",CR ); 282 change = StringUtil.replace( change,"\\t","\t" ); 283 } 284 285 if( delete ) { change = ""; } // 削除は、"" 文字列と置換します。 286 287 // 8.1.3.0 (2022/06/03) Modify 288// if( ignoreCase ) { 289// pattern = Pattern.compile( keyword,Pattern.CASE_INSENSITIVE ); 290// } 291// else { 292// pattern = Pattern.compile( keyword ); 293// } 294 // 大文字小文字を区別しない 295 if( ignoreCase ) { 296 if( useRegexp ) { pattern = Pattern.compile( keyword,Pattern.CASE_INSENSITIVE ); } // 正規表現を使用する 297 else { keyword = keyword.toLowerCase( Locale.JAPAN ); } 298 } 299 // 大文字小文字を区別する 300 else { 301 if( useRegexp ) { pattern = Pattern.compile( keyword ); } // 正規表現を使用しない 302 } 303 304 // jarファイルの接頭辞/接尾辞/部分文字列 305 if( StringUtil.isNotNull(jarPrefix) ){ jarPrefix = jarPrefix.toLowerCase( Locale.JAPAN ); } 306 if( StringUtil.isNotNull(jarSuffix) ){ jarSuffix = jarSuffix.toLowerCase( Locale.JAPAN ); } 307 if( StringUtil.isNotNull(jarInstr) ){ jarInstr = jarInstr.toLowerCase( Locale.JAPAN ); } 308 309 // 8.1.3.0 (2022/06/03) 保存ファイル指定有り 310 if( StringUtil.isNotNull(saveFile) ){ 311 final String filename = HybsSystem.url2dir( StringUtil.urlAppend( fileURL, saveFile ) ); 312 outWriter = FileUtil.getPrintWriter( new File( filename ), ENCODE, true ); 313 } 314 } 315 316 /** 317 * プロセスの終了を行います。最後に一度だけ、呼び出されます。 318 * 終了処理(ファイルクローズ、DBクローズ等)に使用します。 319 * 320 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 321 * 322 * @param isOK トータルで、OKだったかどうか[true:成功/false:失敗] 323 */ 324 public void end( final boolean isOK ) { 325 // 8.1.3.0 (2022/06/03) 保存ファイル指定有り 326 if( StringUtil.isNotNull(saveFile) ){ 327 Closer.ioClose( outWriter ); 328 } 329 } 330 331 /** 332 * 引数の LineModel を処理するメソッドです。 333 * 変換処理後の LineModel を返します。 334 * 後続処理を行わない場合(データのフィルタリングを行う場合)は、 335 * null データを返します。つまり、null データは、後続処理を行わない 336 * フラグの代わりにも使用しています。 337 * なお、変換処理後の LineModel と、オリジナルの LineModel が、 338 * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。 339 * ドキュメントに明記されていない場合は、副作用が問題になる場合は、 340 * 各処理ごとに自分でコピー(クローン)して下さい。 341 * 342 * @og.rev 4.0.1.0 (2007/12/14) ファイルの一括処理対応。 343 * @og.rev 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 344 * @og.rev 6.3.1.0 (2015/06/28) errAbend属性追加。 345 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 346 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 347 * 348 * @param data オリジナルのLineModel 349 * 350 * @return 処理変換後のLineModel 351 */ 352 @Override // ChainProcess 353 public LineModel action( final LineModel data ) { 354 inCount++ ; 355 356 final FileLineModel fileData ; 357 if( data instanceof FileLineModel ) { 358 fileData = (FileLineModel)data ; 359 } 360 else { 361 // これは、プログラマーの問題なので、errAbend 対象外 362 final String errMsg = "データが FileLineModel オブジェクトではありません。" + CR ; 363 throw new OgRuntimeException( errMsg ); 364 } 365 366 final File file = fileData.getFile() ; 367 if( !file.isFile() ) { 368 if( display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 369 return data; 370 } 371 372 boolean isFind = false ; // 6.3.1.0 (2015/06/28) errAbend属性追加に伴う、初期化漏れ対応 373 374 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 375 String errMsg = null; 376 try { 377 String fileLine = null; 378 int firstLineNo = -1; 379 380 // 8.1.3.0 (2022/06/03) 拡張子によるエンコード指定 381 final String mapEnc = getEncode( file ); 382 // 8.1.3.0 (2022/06/03) 該当するエンコード無し 383 if( !"-".equals(mapEnc) ) { 384 // 8.1.3.0 (2022/06/03) jarファイル内の検索 385// if( useBulkRead ) { fileLine = findKeywordAsBulk( file ); } 386// else { firstLineNo = findKeyword( file ); } 387 if( useBulkRead ) { fileLine = findKeywordAsBulk( file,mapEnc ); } 388 else { 389 if( file.getName().endsWith( ".jar" ) ) { findJarKeyword( file ); } // firstLineNo は、常に -1 390 else { firstLineNo = findKeyword( file,mapEnc ); } 391 } 392 } 393 394 isFind = fileLine != null || firstLineNo >= 0 ; 395 396 // 置換処理 ただし、見つかったときのみ実行 397 if( change != null && isFind ) { 398 // 入力ファイルは、オリジナル_backup ファイルとする。過去のファイルを削除 399 final File inFile = new File( file.getPath() + "_backup" ); 400 if( inFile.exists() && !inFile.delete() ) { 401 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 402// final String errMsg = "過去のBKUPファイルを削除できませんでした。[" + inFile + "]" + CR 403 errMsg = "過去のBKUPファイルを削除できませんでした。[" + inFile + "]" + CR 404 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 405 // try の中から throw するのは、行儀がよくないが、catch ブロックで errAbend処理する。 406// throw new OgRuntimeException( errMsg ); 407 } 408 409 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 410 if( errMsg == null ) { 411 // オリジナルのファイルを、_backup ファイル名に先に変換する。 412 final File fromFile = new File( file.getPath() ); 413 if( !fromFile.renameTo( inFile ) ) { 414 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 415// final String errMsg = "所定のファイルをリネームできませんでした。[" + fromFile + "]" + CR 416 errMsg = "所定のファイルをリネームできませんでした。[" + fromFile + "]" + CR 417 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 418 // try の中から throw するのは、行儀がよくないが、catch ブロックで errAbend処理する。 419// throw new OgRuntimeException( errMsg ); 420 } 421 } 422 423 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 424 if( errMsg == null ) { 425 // 変換処理 本体 426 if( useBulkRead ) { changeKeywordAsBulk( fileLine,file ); } 427// else { changeKeyword( inFile,file,firstLineNo ); } 428 else { changeKeyword( inFile,file,firstLineNo,mapEnc ); } // 8.1.3.0 (2022/06/03) 429 430 // backup を使わない場合は、削除する。 431 // 4.0.0.0 (2007/11/29) 入れ子if の統合 432 if( !useBackup && !inFile.delete() ) { 433 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 434// final String errMsg = "所定のファイルを削除できませんでした。[" + inFile + "]" + CR 435 errMsg = "所定のファイルを削除できませんでした。[" + inFile + "]" + CR 436 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 437 // try の中から throw するのは、行儀がよくないが、catch ブロックで errAbend処理する。 438// throw new OgRuntimeException( errMsg ); 439 } 440 } 441 } 442 } 443 catch( final RuntimeException ex ) { 444 final String errMsg2 = "処理中にエラーが発生しました。[" + data.getRowNo() + "]件目" + CR 445 + "data=[" + data.dataLine() + "]" + CR ; // 5.7.2.2 (2014/01/24) エラー時にデータも出力します。 446 // 6.3.1.0 (2015/06/28) errAbend属性追加。 447 throwException( errMsg2,ex,errAbend ); 448 } 449 450 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 451 if( errMsg != null ) { 452 throw new OgRuntimeException( errMsg ); 453 } 454 455// if( display && ( notEquals ^ isFind ) ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 456 if( notEquals ^ isFind && display ) { println( data.dataLine() ); } // 5.1.2.0 (2010/01/01) display の条件変更 // 6.9.7.0 (2018/05/14) PMD Useless parentheses. 457 return notEquals ^ isFind ? data : null ; 458 } 459 460 /** 461 * キーワードが存在しているかどうかをチェックします。 462 * ここでは、1行づつ読み取りながら、すべてのキーワードをピックアップします。 463 * 464 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 465 * @og.rev 8.5.2.0 (2023/07/14) -change=置換文字列 の指定がなくても findCount がセットされるように対応 466 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 467 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 468 * 469 * @param file 検索元のファイルオブジェクト 470 */ 471 private void findJarKeyword( final File file ) { 472 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 473// JarFile jarFile = null; 474 475// try { 476// jarFile = new JarFile( file ); 477 478 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 479 String errMsg = null; 480 481 try ( JarFile jarFile = new JarFile( file ) ) { 482 final Enumeration<JarEntry> flEnum = jarFile.entries() ; // Generics警告対応 483 while( flEnum.hasMoreElements() ) { 484 final JarEntry ent = flEnum.nextElement(); // Generics警告対応 485 if( ent.isDirectory() ) { continue; } 486 487 final String fileName = ent.getName(); // jarファイル内のファイル 488 489 // 拡張子によるエンコード指定 490 final String mapEnc = getEncode( fileName ); 491 // 該当するエンコード無し 492 if( "-".equals(mapEnc) ) { continue; } 493 494 final String lowName = fileName.toLowerCase( Locale.JAPAN ); 495 if( ( jarPrefix == null || lowName.startsWith( jarPrefix ) ) && 496 ( jarSuffix == null || lowName.endsWith( jarSuffix ) ) && 497 ( jarInstr == null || lowName.contains( jarInstr ) ) ) { 498 499 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 500 InputStream stream = null; 501 BufferedReader reader = null; 502 try { 503 stream = jarFile.getInputStream( ent ) ; 504// try ( InputStream stream = jarFile.getInputStream( ent ) ; 505// BufferedReader reader = new BufferedReader( new InputStreamReader( stream, mapEnc ) ) ) { 506 reader = new BufferedReader( new InputStreamReader( stream, mapEnc ) ); 507 508 final CommentLineParser clp = useOmitCmnt ? new CommentLineParser( FileInfo.getSUFIX( fileName ) ) : null; 509// try { 510 String line ; 511 int lineNo = 0; 512 while((line = reader.readLine()) != null) { 513 lineNo++ ; // 注意:ここで返す行数は、コメント行を含む行数とする 514 515 // useOmitCmnt 機能(コメント行を削除する処理を入れる) 516 if( useOmitCmnt ) { 517 line = clp.line( line ); 518 if( line == null ) { continue; } // 戻り値が null の場合は、行として不成立 519 } 520 521 // キーワードを含むかどうか判定 522 if( isKeyword( line ) ) { 523 final String msg = file.getPath() + "\\" + fileName + '(' + lineNo + "):" + line ; 524 if( debug ) { 525 final String buf = "DEBUG:\t" + msg ; 526 println( buf ); 527 } 528 // useAllFind=true 相当の処理のみ行う 529 println( msg ); 530 findCount++ ; // 8.5.2.0 (2023/07/14) Add 531 // 保存ファイル指定有り 532 if( StringUtil.isNotNull(saveFile) ){ outWriter.println( msg ); } 533 534 // useAllFind 機能(最後まで検索を続ける) 535 if( !useAllFind ) { break; } 536 } 537 } 538// } 539 } 540 // nioを使用すると UTF-8とShuft-JISで、エラーになる 541 catch( final CharacterCodingException ex ) { 542 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 543// final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 544 errMsg = "文字のエンコード・エラーが発生しました。" + CR 545 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 546 + ex.getMessage() + CR 547 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 548// + " [" + file.getPath() + "] , Encode=[" + mapEnc + "]" ; 549 + pathEncode( file, mapEnc ); 550// // 呼出元で errAbend 処理するので、そのまま throw しておく 551// throw new OgCharacterException( errMsg,ex ); 552 break; 553 } 554 catch( final IOException ex ) { 555 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 556// final String errMsg = "キーワードファイル読取エラーが発生しました。" + CR 557 errMsg = "キーワードファイル読取エラーが発生しました。" + CR 558 + ex.getMessage() + CR 559 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 560// + " [" + file.getPath() + "] , Encode=[" + mapEnc + "]" ; 561 + pathEncode( file, mapEnc ); 562// // 呼出元で errAbend 処理するので、そのまま throw しておく 563// throw new OgRuntimeException( errMsg,ex ); 564 break; 565 } 566// finally { 567// Closer.ioClose( reader ); 568// } 569// } 570 finally { 571 Closer.ioClose( reader ); 572 Closer.ioClose( stream ); 573 } 574 } 575 } 576 } 577 catch( final IOException ex ) { 578 final String errMsg2 = "キーワードファイル読取エラーが発生しました。" + CR 579 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 580// + " [" + file.getPath() + "] , Encode=[" + inEncode + "]" ; 581 + pathEncode( file, inEncode ); 582 // 呼出元で errAbend 処理するので、そのまま throw しておく 583 throw new OgRuntimeException( errMsg2,ex ); 584 } 585// finally { 586// Closer.zipClose( jarFile ); 587// } 588 589 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ExceptionAsFlowControl 対応 590 if( errMsg != null ) { 591 throw new OgRuntimeException( errMsg ); 592 } 593 } 594 595 /** 596 * 拡張子をエンコードに変換します。 597 * 598 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 599 * 600 * @param file 検索元のファイル 601 * @return エンコード 602 */ 603 private String getEncode( final File file ) { 604 return getEncode( file.getName() ); 605 } 606 607 /** 608 * 拡張子をエンコードに変換します。 609 * 610 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 611 * 612 * @param fileName 検索元のファイル名 613 * @return エンコード 614 */ 615 private String getEncode( final String fileName ) { 616 if( AUTO_ENCODE.equalsIgnoreCase( inEncode ) ) { // 自動判定 617 final String sufix = FileInfo.getSUFIX( fileName ); // 拡張子取得 618 return StringUtil.nval( EXT2ENC.get( sufix ), "-" ); 619 } 620 else { 621 return StringUtil.nval( inEncode, "-" ); 622 } 623 } 624 625 /** 626 * 該当の行にキーワードを含むかどうか判定します。 627 * 628 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 629 * 630 * @param line 検索元のファイルの行 631 * @return キーワードを含むかどうか[true/false] 632 */ 633 private boolean isKeyword( final String line ) { 634 if( useRegexp ){ // 正規表現を使用する 635 final Matcher mach = pattern.matcher( line ); 636 return mach.find(); 637 } 638 639 // 8.5.5.1 (2024/02/29) PMD 7.0.0 OnlyOneReturn メソッドには終了ポイントが 1 つだけ必要 640 return ignoreCase 641 ? line.toLowerCase( Locale.JAPAN ).contains( keyword ) // 大文字小文字を区別しない 642 : line.contains( keyword ); // 大文字小文字を区別する 643 644// // 正規表現を使用しない 645// else { 646// // 大文字小文字を区別しない 647// if( ignoreCase ) { 648// return line.toLowerCase( Locale.JAPAN ).contains( keyword ); 649// } 650// // 大文字小文字を区別する 651// else { 652// return line.contains( keyword ); 653// } 654// } 655 } 656 657 /** 658 * キーワードが存在しているかどうかをチェックします。 659 * ここでは、1行づつ読み取りながら、最初に見つかった時点で制御を返します。 660 * よって、複数行にまたがる keyword でのマッチングは出来ませんが、大きな 661 * ファイル等での検索には、効率的です。 662 * 663 * @og.rev 4.0.1.0 (2007/12/14) 新規追加 664 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 665 * @og.rev 6.3.1.0 (2015/06/28) errAbend属性追加。 666 * @og.rev 6.3.1.1 (2015/07/10) useOmitCmnt、useAllFind 機能追加 667 * @og.rev 6.4.0.2 (2015/12/11) CommentLineParser 改造。 668 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 669 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 670 * @og.rev 8.5.2.0 (2023/07/14) -change=置換文字列 の指定がなくても findCount がセットされるように対応 671 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 672 * 673 * @param file 検索元のファイルオブジェクト 674 * @param mapEnc 検索元のファイルエンコード 675 * 676 * @return 最初に見つかった行番号(見つからなければ、-1 を返す) 677 */ 678// private int findKeyword( final File file ) { 679 private int findKeyword( final File file ,final String mapEnc ) { // 8.1.3.0 (2022/06/03) 680 int firstLineNo = -1; 681// final BufferedReader reader = FileUtil.getBufferedReader( file,inEncode ); 682 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 683// final BufferedReader reader = FileUtil.getBufferedReader( file,mapEnc ); // 8.1.3.0 (2022/06/03) 684 685 // 6.4.0.2 (2015/12/11) CommentLineParser 改造 686 final CommentLineParser clp = useOmitCmnt ? new CommentLineParser( FileInfo.getSUFIX( file ) ) : null; 687// try { 688 try ( BufferedReader reader = FileUtil.getBufferedReader( file,mapEnc ) ) { // 8.1.3.0 (2022/06/03) 689 String line ; 690 int lineNo = 0; 691 while((line = reader.readLine()) != null) { 692 lineNo++ ; // 注意:ここで返す行数は、コメント行を含む行数とする。 693 694 // 6.3.1.1 (2015/07/10) useOmitCmnt 機能。コメント行を削除する処理を入れる。 695 if( useOmitCmnt ) { 696 line = clp.line( line ); 697 if( line == null ) { continue; } // 戻り値が null の場合は、行として不成立 698 } 699 // 8.1.3.0 (2022/06/03) Modify 700// final Matcher mach = pattern.matcher( line ); 701// if( mach.find() ) { 702 // キーワードを含むかどうか判定 703 if( isKeyword( line ) ) { 704 if( debug ) { 705 final String buf = "DEBUG:\t" + file.getPath() + "(" + lineNo + "): " + line ; 706 println( buf ); 707 } 708 709 // 6.3.1.1 (2015/07/10) useAllFind 機能。最後まで検索を続けます。 710 if( useAllFind ) { 711 final String msg = file.getAbsolutePath() + '(' + lineNo + "):" + line ; 712 println( msg ); 713 findCount++ ; // 8.5.2.0 (2023/07/14) Add 714 // 8.1.3.0 (2022/06/03) 保存ファイル指定有り 715 if( StringUtil.isNotNull(saveFile) ){ outWriter.println( msg ); } 716 } 717 else { 718 firstLineNo = lineNo; 719 break; 720 } 721 } 722 } 723 } 724 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 725 catch( final CharacterCodingException ex ) { 726 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 727 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 728// + " [" + file.getPath() + "] , Encode=[" + inEncode + "]" ; 729 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 730// + " [" + file.getPath() + "] , Encode=[" + mapEnc + "]" ; // 8.1.3.0 (2022/06/03) 731 + pathEncode( file, mapEnc ); 732 // 呼出元で、errAbend処理するので、そのまま、throw しておく。 733 throw new OgCharacterException( errMsg,ex ); // 6.5.0.1 (2016/10/21) 734 } 735 catch( final IOException ex ) { 736 final String errMsg = "キーワードファイル読取エラーが発生しました。" + CR 737// + " [" + file.getPath() + "] , Encode=[" + inEncode + "]" ; 738 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 739// + " [" + file.getPath() + "] , Encode=[" + mapEnc + "]" ; // 8.1.3.0 (2022/06/03) 740 + pathEncode( file, mapEnc ); 741 // 呼出元で、errAbend処理するので、そのまま、throw しておく。 742 throw new OgRuntimeException( errMsg,ex ); 743 } 744// finally { 745// Closer.ioClose( reader ); 746// } 747 748 return firstLineNo; 749 } 750 751 /** 752 * キーワードが存在しているかどうかをチェックします。 753 * ここでは、ファイルをすべて読み取ってから、チェックします。 754 * よって、複数行にまたがる keyword でのマッチングが可能です。 755 * 756 * @og.rev 4.0.1.0 (2007/12/14) 新規追加 757 * @og.rev 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 758 * @og.rev 6.4.5.2 (2016/05/06) fukurou.util.FileString から、fukurou.util.FileUtil に移動。 759 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 760 * @og.rev 8.5.2.0 (2023/07/14) -change=置換文字列 の指定がなくても findCount がセットされるように対応 761 * 762 * @param file 検索元のファイルオブジェクト 763 * @param mapEnc 検索元のファイルエンコード 764 * 765 * @return 検索元のファイルの文字列化情報(ただし、見つからなければ、null) 766 */ 767// private String findKeywordAsBulk( final File file ) { 768 private String findKeywordAsBulk( final File file,final String mapEnc ) { // 8.1.3.0 (2022/06/03) 769 770 boolean isFind = false; 771 772 // 6.4.5.1 (2016/04/28) FileStringのコンストラクター変更 773// final String line = FileUtil.getValue( file.getPath() , inEncode ); // 6.4.5.2 (2016/05/06) 774 final String line = FileUtil.getValue( file.getPath() , mapEnc ); // 8.1.3.0 (2022/06/03) 775 776 final Matcher mach = pattern.matcher( line ); 777 if( mach.find() ) { 778 if( debug ) { println( "DEBUG:\t" + file.getPath() ); } 779 findCount++ ; // 8.5.2.0 (2023/07/14) Add 780 isFind = true; 781 } 782 783 return isFind ? line : null; 784 } 785 786 /** 787 * キーワードを指定の文字列に置き換えます。 788 * useBackup 属性に true を指定した場合、置き換え後の、backup ファイルは、 789 * オリジナル_backup という名称に変わります。 790 * ここでは、1行づつ読み取りながら、変換処理を行います。 791 * よって、複数行にまたがる keyword でのマッチングは出来ませんが、大きな 792 * ファイル等での置換でも、メモリの使用量は抑えられます。 793 * 794 * @og.rev 4.0.1.0 (2007/12/14) 置換処理を独立させます。 795 * @og.rev 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 796 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 797 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。 798 * @og.rev 8.1.3.0 (2022/06/03) jarファイル内の検索、オートエンコード対応 799 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 800 * 801 * @param inFile 検索元の入力ファイルオブジェクト 802 * @param outFile 変換後の出力ファイルオブジェクト 803 * @param firstLineNo キーワードが存在した場合の最初の行番号 804 * @param mapEnc 検索元のファイルエンコード 805 */ 806// private void changeKeyword( final File inFile,final File outFile,final int firstLineNo ) { 807 private void changeKeyword( final File inFile,final File outFile,final int firstLineNo,final String mapEnc ) { // 8.1.3.0 (2022/06/03) 808 809// final BufferedReader reader = FileUtil.getBufferedReader( inFile,inEncode ); 810 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 811 final BufferedReader reader = FileUtil.getBufferedReader( inFile,mapEnc ); // 8.1.3.0 (2022/06/03) 812 final PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ); 813 814 String line = null; 815 try { 816// try ( BufferedReader reader = FileUtil.getBufferedReader( inFile,mapEnc ); // 8.1.3.0 (2022/06/03) 817// PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ) ) { 818 // 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 819 if( "HEAD".equals( insert ) ) { 820 writer.println( change ); 821 } 822 823 int lineNo = 0; 824 while((line = reader.readLine()) != null) { 825 lineNo++ ; 826 if( lineNo <= skipRowCount ) { continue; } // 6.2.4.0 (2015/05/15) 827 828 if( lineNo >= firstLineNo ) { 829 final Matcher mach = pattern.matcher( line ); 830 831 String chnStr = null; 832 if( "CHANGE".equals( insert ) ) { 833 chnStr = strChange( mach ); 834 } 835 else if( "BEFORE".equals( insert ) ) { 836 chnStr = strBefore( line,mach ); 837 } 838 else if( "AFTER".equals( insert ) ) { 839 chnStr = strAfter( line,mach ); 840 } 841 842 if( chnStr != null ) { 843 line = chnStr; 844 cngCount++ ; // 変換されれば カウント 845 } 846 } 847 writer.println( line ); // readLine() してるので、最後に改行が必要。 848 } 849 850 // 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 851 if( "TAIL".equals( insert ) ) { 852 writer.println( change ); 853 } 854 } 855 // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。 856 catch( final CharacterCodingException ex ) { 857 final String errMsg = "文字のエンコード・エラーが発生しました。" + CR 858 + " ファイルのエンコードが指定のエンコードと異なります。" + CR 859// + " [" + inFile + "] , Encode=[" + inEncode + "]" ; 860 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 861// + " [" + inFile + "] , Encode=[" + mapEnc + "]" ; // 8.1.3.0 (2022/06/03) 862 + pathEncode( inFile, mapEnc ); 863 // 呼出元で、errAbend処理するので、そのまま、throw しておく。 864 throw new OgCharacterException( errMsg,ex ); // 6.5.0.1 (2016/10/21) 865 } 866 catch( final IOException ex ) { 867 final String errMsg = "処理中にエラーが発生しました。[" + line + "]" + CR 868// + " [" + inFile + "] , Encode=[" + inEncode + "]" ; 869 // 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 870// + " [" + inFile + "] , Encode=[" + mapEnc + "]" ; // 8.1.3.0 (2022/06/03) 871 + pathEncode( inFile, mapEnc ); 872 // 呼出元で、errAbend処理するので、そのまま、throw しておく。 873 throw new OgRuntimeException( errMsg,ex ); 874 } 875 finally { 876 Closer.ioClose( reader ); 877 Closer.ioClose( writer ); 878 } 879 } 880 /** 881 * キーワードを指定の文字列に置き換えます。 882 * useBackup 属性に true を指定した場合、置き換え後の、backup ファイルは、 883 * オリジナル_backup という名称に変わります。 884 * ここでは、ファイルをすべて読み取ってから、チェックします。 885 * よって、複数行にまたがる keyword でのマッチングが可能です。 886 * 887 * @og.rev 4.0.1.0 (2007/12/14) 置換処理を独立させます。 888 * @og.rev 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 889 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 890 * 891 * @param fileLine 検索元の行文字列 892 * @param outFile 出力ファイルオブジェクト 893 */ 894 private void changeKeywordAsBulk( final String fileLine,final File outFile ) { 895 // 8.5.4.2 (2024/01/12) PMD 7.0.0 CloseResource 対応 896// final PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ); 897 898 String line = fileLine ; 899// try { 900 try ( PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode ) ) { 901 // 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 902 if( "HEAD".equals( insert ) ) { 903 writer.println( change ); 904 } 905 906 final Matcher mach = pattern.matcher( line ); 907 908 String chnStr = null; 909 if( "CHANGE".equals( insert ) ) { 910 chnStr = strChange( mach ); 911 } 912 else if( "BEFORE".equals( insert ) ) { 913 chnStr = strBefore( line,mach ); 914 } 915 else if( "AFTER".equals( insert ) ) { 916 chnStr = strAfter( line,mach ); 917 } 918 919 if( chnStr != null ) { 920 line = chnStr; 921 cngCount++ ; // 変換されれば カウント 922 } 923 924 writer.print( line ); // 注意:改行コードは、不要 925 926 // 6.2.4.0 (2015/05/15) HEAD,TAIL 追加 927 if( "TAIL".equals( insert ) ) { 928 writer.println( change ); 929 } 930 } 931 catch( final RuntimeException ex ) { 932 final String errMsg = "処理中にエラーが発生しました。[" + outFile.getPath() + "]" ; 933 // 呼出元で、errAbend処理するので、そのまま、throw しておく。 934 throw new OgRuntimeException( errMsg,ex ); 935 } 936// finally { 937// Closer.ioClose( writer ); 938// } 939 } 940 941 /** 942 * insert が、"CHANGE" の場合の処理結果を求めます。 943 * 変換しなかった場合は、null を返します。 944 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 945 * 946 * @param mach キーワードの正規表現 947 * 948 * @return 変換結果(対象行で無い場合は、null) 949 */ 950 private String strChange( final Matcher mach ) { 951 String line = null; 952 if( mach.find() ) { 953 line = mach.replaceAll( change ); 954 } 955 return line ; 956 } 957 958 /** 959 * insert が、"BEFORE" の場合の処理結果を求めます。 960 * 変換しなかった場合は、null を返します。 961 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 962 * 963 * @param line 検索行 964 * @param mach キーワードの正規表現 965 * 966 * @return 変換結果(対象行で無い場合は、null) 967 */ 968 private String strBefore( final String line , final Matcher mach ) { 969 boolean isChng = false; 970 final StringBuilder buf = new StringBuilder( line.length() ); 971 int indx = 0; 972 while( mach.find() ) { 973 isChng = true; 974 final int strt = mach.start() + insOffset; 975 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 976 buf.append( line.substring( indx,strt ) ) 977 .append( change ); 978 indx = strt; 979 } 980 981 String rtn = null; 982 if( isChng ) { 983 buf.append( line.substring( indx ) ); 984 rtn = buf.toString(); 985 } 986 987 return rtn ; 988 } 989 990 /** 991 * insert が、"AFTER" の場合の処理結果を求めます。 992 * 変換しなかった場合は、null を返します。 993 * これは、変換カウントを算出する為のフラグ代わりに使用しています。 994 * 995 * @param line 検索行 996 * @param mach キーワードの正規表現 997 * 998 * @return 変換結果(対象行で無い場合は、null) 999 */ 1000 private String strAfter( final String line , final Matcher mach ) { 1001 boolean isChng = false; 1002 final StringBuilder buf = new StringBuilder( line.length() ); 1003 int indx = 0; 1004 while( mach.find() ) { 1005 isChng = true; 1006 final int end = mach.end() + insOffset; 1007 // 8.5.4.2 (2024/01/12) PMD 7.0.0 ConsecutiveAppendsShouldReuse 対応 1008 buf.append( line.substring( indx,end ) ) 1009 .append( change ); 1010 indx = end; 1011 } 1012 String rtn = null; 1013 if( isChng ) { 1014 buf.append( line.substring( indx ) ); 1015 rtn = buf.toString(); 1016 } 1017 1018 return rtn ; 1019 } 1020 1021 /** 1022 * 引数の File とエンコードを文字列に変換します。 1023 * エラーメッセージ等に使用することを前提としています。 1024 * 1025 * @og.rev 8.5.4.2 (2024/01/12) PMD 7.0.0 AvoidDuplicateLiterals 対応 1026 * 1027 * @param file 検索行 1028 * @param encode キーワードの正規表現 1029 * 1030 * @return 状況を表示する文字列に変換する。 1031 */ 1032 private String pathEncode( final File file , final String encode ) { 1033 return " [" + file.getPath() + "] , Encode=[" + encode + "]" ; // 8.1.3.0 (2022/06/03) 1034 } 1035 1036 /** 1037 * プロセスの処理結果のレポート表現を返します。 1038 * 処理プログラム名、入力件数、出力件数などの情報です。 1039 * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような 1040 * 形式で出してください。 1041 * 1042 * @return 処理結果のレポート 1043 */ 1044 public String report() { 1045 if( findCount < cngCount ) { findCount = cngCount; } 1046 1047 // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX' 1048 return "[" + getClass().getName() + "]" + CR 1049// final String report = "[" + getClass().getName() + "]" + CR 1050 + TAB + "Search Keyword : " + keyword + CR 1051 + TAB + "Search File Count : " + inCount + CR 1052 + TAB + "Key Find Count : " + findCount + CR 1053 + TAB + "Key Change Count : " + cngCount ; 1054 1055// return report ; 1056 } 1057 1058 /** 1059 * このクラスの使用方法を返します。 1060 * 1061 * @return このクラスの使用方法 1062 * @og.rtnNotNull 1063 */ 1064 public String usage() { 1065 final StringBuilder buf = new StringBuilder( 1400 ) 1066 .append( "Process_Grep は、上流から受け取った FileLineModelから、文字列を見つけ出す" ).append( CR ) 1067 .append( "ChainProcess インターフェースの実装クラスです。" ).append( CR ) 1068 .append( CR ) 1069 .append( "正規表現の keyword を上流から受け取った FileLineModel から検索します。" ).append( CR ) 1070 .append( "見つかった対象ファイルから、指定の文字列を置換する場合は、-change か" ).append( CR ) 1071 .append( "-changeFile で、keyword を置換する文字列を指定して下さい。" ).append( CR ) 1072 .append( "置換する文字列には、\t と \n の特殊文字が使用できます。" ).append( CR ) 1073 .append( CR ) 1074 .append( "処理対象は、通常は、1行づつ読み取りながら処理を行います。存在チェックの場合は、" ).append( CR ) 1075 .append( "見つかった時点で処理を中止します。これは、該当箇所をピックアップするのではなく、" ).append( CR ) 1076 .append( "存在しているかどうかを判断して、あれば、下流に流すというのが目的だからです。" ).append( CR ) 1077 .append( "keyword を、改行を含む正規表現で、検索・置換する場合は、-useBulkRead 属性を" ).append( CR ) 1078 .append( "true に設定してください。これは、入力ファイルを一括して読み込みます。" ).append( CR ) 1079 .append( "-ignoreCase は、検索時にキーの大文字小文字を無視するように指定します。" ).append( CR ) 1080 .append( "-notEquals は、結果(見つかればtrue)を反転(見つからなければtrue)します。" ).append( CR ) 1081 .append( "これは、行単位ではなく、ファイル単位に判定しますので、change 指定した場合" ).append( CR ) 1082 .append( "でも、対象行は、見つかった行です。ただし、下流に対して、見つからない" ).append( CR ) 1083 .append( "場合だけ処理を継続させます。" ).append( CR ) 1084 .append( "-inEncode は、入力ファイルのエンコード指定になります。" ).append( CR ) 1085 .append( "-outEncode は、出力ファイルのエンコードや、changeFileで指定の置換文字列" ).append( CR ) 1086 .append( "ファイルのエンコード指定になります。(changeFile は、必ず 出力ファイルと)" ).append( CR ) 1087 .append( "同じエンコードです。" ).append( CR ) 1088 .append( "これらのエンコードが無指定の場合は、System.getProperty(\"file.encoding\") " ).append( CR ) 1089 .append( "で求まる値を使用します。" ).append( CR ) 1090 .append( "-changeFile を使用することで、複数行の文字列に置換することが可能です。" ).append( CR ) 1091 .append( CR ) 1092 .append( "上流(プロセスチェインのデータは上流から渡されます。)からのLineModel の" ).append( CR ) 1093 .append( "ファイルオブジェクトより、指定の文字列が含まれているか検索します。" ).append( CR ) 1094 .append( "上流プロセスでは、Name 属性として、『File』を持ち、値は、Fileオブジェクト" ).append( CR ) 1095 .append( "である、Process_FileSearch を使用するのが、便利です。それ以外のクラスを" ).append( CR ) 1096 .append( "使用する場合でも、Name属性と、File オブジェクトを持つ LineModel を受け渡し" ).append( CR ) 1097 .append( "できれば、使用可能です。" ).append( CR ) 1098 .append( CR ) 1099// .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。" ).append( CR ) 1100// .append( "引数文字列の 『=』 の前後には、空白は挟めません。必ず、-key=value の様に" ).append( CR ) 1101// .append( "繋げてください。" ).append( CR ) 1102 .append( PROCESS_PARAM_USAGE ) // 8.5.6.1 (2024/03/29) 継承元使用 1103 .append( CR ).append( CR ) 1104 .append( getArgument().usage() ).append( CR ); 1105 1106 return buf.toString(); 1107 } 1108 1109 /** 1110 * このクラスは、main メソッドから実行できません。 1111 * 1112 * @param args コマンド引数配列 1113 */ 1114 public static void main( final String[] args ) { 1115 LogWriter.log( new Process_Grep().usage() ); 1116 } 1117}