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     */
016    package org.opengion.fukurou.process;
017    
018    import org.opengion.fukurou.util.Argument;
019    import org.opengion.fukurou.util.FileUtil;
020    import org.opengion.fukurou.util.FileString;
021    import org.opengion.fukurou.util.Closer ;
022    import org.opengion.fukurou.util.StringUtil ;
023    import org.opengion.fukurou.util.LogWriter;
024    
025    import java.util.Arrays;
026    import java.util.Map ;
027    import java.util.LinkedHashMap ;
028    import java.util.regex.Pattern;
029    import java.util.regex.Matcher;
030    
031    import java.io.File;
032    import java.io.PrintWriter;
033    import java.io.BufferedReader;
034    import 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     * 処ç�?¯¾象はã€??常はã€?¼‘行づつ読み取りながら処ç�?‚’行いますã?存在チェãƒ?‚¯の場合ã?ã€?
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     */
098    public 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             * 初期処ç�?ファイルオープンã€?¼¤?¢オープンç­?に使用しますã?
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    //              if( debug ) { println( arg.toString() ); }                      // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
180    
181                    if( change != null ) {
182                            int adrs = insert.indexOf( ' ' );       // オフセãƒ?ƒˆ数字ã?有無
183                            if( adrs > 0 ) {
184                                    insOffset = Integer.parseInt( insert.substring( adrs+1 ) );
185                                    insert    = insert.substring( 0,adrs );
186                            }
187    
188                            boolean isOK = false;
189                            for( int i=0; i<INSERT_LIST.length; i++ ) {
190                                    if( insert.equalsIgnoreCase( INSERT_LIST[i] ) ) {
191                                            isOK = true; break;
192                                    }
193                            }
194                            if( !isOK ) {
195                                    String errMsg = "insert はã€? + Arrays.toString( INSERT_LIST )
196                                                                            + " からæŒ?®šしてくださいã€? + CR
197                                                                            + "-insert=[" + insert + "]" ;
198                                    throw new RuntimeException( errMsg );
199                            }
200    
201                            change = StringUtil.replace( change,"\\n",CR );
202                            change = StringUtil.replace( change,"\\t","\t" );
203                    }
204    
205                    if( delete ) { change = ""; }   // 削除はã€?" æ–?­—å?と置換しますã?
206    
207                    if( ignoreCase ) {
208                            pattern = Pattern.compile( keyword,Pattern.CASE_INSENSITIVE );
209                    }
210                    else {
211                            pattern = Pattern.compile( keyword );
212                    }
213            }
214    
215            /**
216             * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
217             * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç­?に使用しますã?
218             *
219             * @param   isOK トã?タルで、OKã�?�£たかどã�?�‹[true:成功/false:失敗]
220             */
221            public void end( final boolean isOK ) {
222                    // ここでは処ç�?‚’行いませんã€?
223            }
224    
225            /**
226             * 引数の LineModel をå?ç�?�™るメソãƒ?ƒ‰ですã?
227             * 変換処ç�?¾Œã? LineModel を返しますã?
228             * 後続å?ç�?‚’行わなã�??å�?ãƒ??タのフィルタリングを行う場å�?はã€?
229             * null ãƒ??タを返しますã?つまりã?null ãƒ??タは、後続å?ç�?‚’行わなã�?
230             * フラグの代わりにも使用してã�?�¾すã?
231             * なおã?変換処ç�?¾Œã? LineModel と、オリジナルの LineModel がã?
232             * 同ä¸?�‹、コピã?(クローン)かã?、各処ç�?ƒ¡ソãƒ?ƒ‰å†?�§決めてã�?�¾すã?
233             * ドキュメントに明記されてã�?�ªã�??合ã?、副作用が問題になるå?合ã?ã€?
234             * å�??ç�?�”とに自åˆ?�§コピã?(クローン)して下さã�??
235             *
236             * @og.rev 4.0.1.0 (2007/12/14) ファイルのä¸?‹¬処ç�?¯¾応ã?
237             * @og.rev 5.7.2.2 (2014/01/24) エラー時にãƒ??タもå?力しますã?
238             *
239             * @param       data    オリジナルのLineModel
240             *
241             * @return      処ç�?¤‰換後ã?LineModel
242             */
243            public LineModel action( final LineModel data ) {
244                    inCount++ ;
245    
246                    final FileLineModel fileData ;
247                    if( data instanceof FileLineModel ) {
248                            fileData = (FileLineModel)data ;
249                    }
250                    else {
251                            String errMsg = "ãƒ??タã�?FileLineModel オブジェクトではありませんã€? + CR ;
252                            throw new RuntimeException( errMsg );
253                    }
254    
255                    File file = fileData.getFile() ;
256                    if( ! file.isFile() ) {
257                            if( display ) { println( data.dataLine() ); }           // 5.1.2.0 (2010/01/01) display の条件変更
258                            return data;
259                    }
260    
261                    final boolean isFind ;
262                    try {
263                            String fileLine = null;
264                            int firstLineNo = -1;
265                            if( useBulkRead ) { fileLine    = findKeywordAsBulk( file ); }
266                            else                      { firstLineNo = findKeyword( file ); }
267    
268                            isFind = ( fileLine != null ) || ( firstLineNo >= 0 ) ;
269    
270                            // 置換å?ç�?ただしã?見つかったときã?み実è¡?
271                            if( change != null && isFind ) {
272                                    // 入力ファイルは、オリジナル_backup ファイルとする。過去のファイルを削除
273                                    File inFile = new File( file.getPath() + "_backup" );
274                                    if( inFile.exists() && !inFile.delete() ) {
275                                            String errMsg = "過去のBKUPファイルを削除できませんでしたã€?" + inFile + "]" + CR
276                                                                    +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時にãƒ??タもå?力しますã?
277                                            throw new RuntimeException( errMsg );
278                                    }
279    
280                                    // オリジナルのファイルをã?_backup ファイル名に先に変換するã€?
281                                    File fromFile = new File( file.getPath() );
282                                    if( !fromFile.renameTo( inFile ) ) {
283                                            String errMsg = "æ‰?®šã?ファイルをリネã?ãƒ?�§きませんでしたã€?" + fromFile + "]" + CR
284                                                                    +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時にãƒ??タもå?力しますã?
285                                            throw new RuntimeException( errMsg );
286                                    }
287    
288                                    // 変換処ç�?本ä½?
289                                    if( useBulkRead ) { changeKeywordAsBulk( fileLine,file ); }
290                                    else                      { changeKeyword( inFile,file,firstLineNo ); }
291    
292                                    // backup を使わなã�??合ã?、削除するã€?
293                                    // 4.0.0.0 (2007/11/29) 入れ子if の統å�?
294                                    if( ! useBackup && !inFile.delete() ) {
295                                            String errMsg = "æ‰?®šã?ファイルを削除できませんでしたã€?" + inFile + "]" + CR
296                                                                    +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時にãƒ??タもå?力しますã?
297                                            throw new RuntimeException( errMsg );
298                                    }
299                            }
300                    }
301                    catch ( RuntimeException ex ) {
302                            String errMsg = "処ç�?¸­にエラーが発生しましたã€?" + data.getRowNo() + "]件目" + CR
303    //                                              + data.toString() ;
304                                                    +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時にãƒ??タもå?力しますã?
305                            throw new RuntimeException( errMsg,ex );
306                    }
307    
308                    if( display && ( notEquals ^ isFind ) ) { println( data.dataLine() ); }         // 5.1.2.0 (2010/01/01) display の条件変更
309                    return ( notEquals ^ isFind ) ? data : null ;
310            }
311    
312            /**
313             * キーワードが存在してã�?‚‹かどã�?�‹をチェãƒ?‚¯しますã?
314             * ここではã€?¼‘行づつ読み取りながら、最初に見つかった時点で制御を返しますã?
315             * よって、è¤?•°行にまたがã‚?keyword でのマッチングは出来ませんがã?大きな
316             * ファイル等での検索には、効çŽ?š„ですã?
317             *
318             * @og.rev 4.0.1.0 (2007/12/14) 新規追åŠ?
319             *
320             * @param       file    検索å…??ファイルオブジェクãƒ?
321             *
322             * @return      æœ??に見つかった行番号(見つからなければã€?1 を返す)
323             */
324            private int findKeyword( final File file ) {
325                    BufferedReader reader = null;
326    
327                    int firstLineNo = -1;
328                    try {
329                            reader = FileUtil.getBufferedReader( file,inEncode );
330                            String line ;
331                            int lineNo = 0;
332                            while((line = reader.readLine()) != null) {
333                                    lineNo++ ;
334                                    Matcher mach = pattern.matcher( line );
335                                    if( mach.find() ) {
336                                            if( debug ) {
337                                                    String buf = "DEBUG:\t" + file.getPath() + "(" + lineNo + "): " + line ;
338                                                    println( buf );
339                                            }
340                                            firstLineNo = lineNo;
341                                            break;
342                                    }
343                            }
344                    }
345                    catch ( IOException ex ) {
346                            String errMsg = "ファイル読取エラーが発生しましたã€?" + file.getPath() + "]" ;
347                            throw new RuntimeException( errMsg,ex );
348                    }
349                    finally {
350                            Closer.ioClose( reader );
351                    }
352    
353                    return firstLineNo;
354            }
355    
356            /**
357             * キーワードが存在してã�?‚‹かどã�?�‹をチェãƒ?‚¯しますã?
358             * ここでは、ファイルをすべて読み取ってから、チェãƒ?‚¯しますã?
359             * よって、è¤?•°行にまたがã‚?keyword でのマッチングが可能ですã?
360             *
361             * @og.rev 4.0.1.0 (2007/12/14) 新規追åŠ?
362             *
363             * @param       file    検索å…??ファイルオブジェクãƒ?
364             *
365             * @return      検索å…??ファイルのæ–?­—å?化情報(ただしã?見つからなければ、null)
366             */
367            private String findKeywordAsBulk( final File file ) {
368    
369                    boolean isFind = false;
370    
371                    FileString sf = new FileString();
372                    sf.setFilename( file.getPath() );
373                    sf.setEncode( inEncode );
374                    String line = sf.getValue();
375    
376                    Matcher mach = pattern.matcher( line );
377                    if( mach.find() ) {
378                            if( debug ) { println( "DEBUG:\t" + file.getPath() ); }
379                            isFind = true;
380                    }
381    
382                    return ( isFind ) ? line : null;
383            }
384    
385            /**
386             * キーワードをæŒ?®šã?æ–?­—å?に置き換えますã?
387             * useBackup 属æ?に true を指定したå?合ã?置き換え後ã?、backup ファイルはã€?
388             * オリジナル_backup とã�?�†名称に変わりますã?
389             * ここではã€?¼‘行づつ読み取りながら、変換処ç�?‚’行いますã?
390             * よって、è¤?•°行にまたがã‚?keyword でのマッチングは出来ませんがã?大きな
391             * ファイル等での置換でもã?メモリの使用量ã?抑えられますã?
392             *
393             * @og.rev 4.0.1.0 (2007/12/14) 置換å?ç�?‚’独立させますã?
394             *
395             * @param       inFile  検索å…??入力ファイルオブジェクãƒ?
396             * @param       outFile 変換後ã?出力ファイルオブジェクãƒ?
397             * @param       firstLineNo     キーワードが存在した場合ã?æœ??の行番号
398             */
399            private void changeKeyword( final File inFile,final File outFile,final int firstLineNo ) {
400    
401                    BufferedReader reader = FileUtil.getBufferedReader( inFile,inEncode );
402                    PrintWriter    writer = FileUtil.getPrintWriter( outFile,outEncode );
403    
404                    String line = null;
405                    try {
406                            int lineNo = 0;
407                            while((line = reader.readLine()) != null) {
408                                    lineNo++ ;
409                                    if( lineNo >= firstLineNo ) {
410                                            Matcher mach = pattern.matcher( line );
411    
412                                            String chnStr = null;
413                                            if( "CHANGE".equals( insert ) ) {
414                                                    chnStr = strChange( mach );
415                                            }
416                                            else if( "BEFORE".equals( insert ) ) {
417                                                    chnStr = strBefore( line,mach );
418                                            }
419                                            else if( "AFTER".equals( insert ) ) {
420                                                    chnStr = strAfter( line,mach );
421                                            }
422    
423                                            if( chnStr != null ) {
424                                                    line = chnStr;
425                                                    cngCount++ ;    // 変換されれã? カウンãƒ?
426                                            }
427                                    }
428                                    writer.println( line ); // readLine() してるã?で、最後に改行がå¿?¦�ã?
429                            }
430                    }
431                    catch ( IOException ex ) {
432                            String errMsg = "処ç�?¸­にエラーが発生しましたã€?" + line + "]" ;
433                            throw new RuntimeException( errMsg,ex );
434                    }
435                    finally {
436                            Closer.ioClose( reader );
437                            Closer.ioClose( writer );
438                    }
439            }
440            /**
441             * キーワードをæŒ?®šã?æ–?­—å?に置き換えますã?
442             * useBackup 属æ?に true を指定したå?合ã?置き換え後ã?、backup ファイルはã€?
443             * オリジナル_backup とã�?�†名称に変わりますã?
444             * ここでは、ファイルをすべて読み取ってから、チェãƒ?‚¯しますã?
445             * よって、è¤?•°行にまたがã‚?keyword でのマッチングが可能ですã?
446             *
447             * @og.rev 4.0.1.0 (2007/12/14) 置換å?ç�?‚’独立させますã?
448             *
449             * @param       fileLine        検索å…??行文字å?
450             * @param       outFile 出力ファイルオブジェクãƒ?
451             */
452            private void changeKeywordAsBulk( final String fileLine,final File outFile ) {
453                    PrintWriter writer = FileUtil.getPrintWriter( outFile,outEncode );
454    
455                    String line = fileLine ;
456                    try {
457                            Matcher mach = pattern.matcher( line );
458    
459                            String chnStr = null;
460                            if( "CHANGE".equals( insert ) ) {
461                                    chnStr = strChange( mach );
462                            }
463                            else if( "BEFORE".equals( insert ) ) {
464                                    chnStr = strBefore( line,mach );
465                            }
466                            else if( "AFTER".equals( insert ) ) {
467                                    chnStr = strAfter( line,mach );
468                            }
469    
470                            if( chnStr != null ) {
471                                    line = chnStr;
472                                    cngCount++ ;    // 変換されれã? カウンãƒ?
473                            }
474    
475                            writer.print( line );   // 注意:改行コードã?、不è¦?
476                    }
477                    catch ( RuntimeException ex ) {
478                            String errMsg = "処ç�?¸­にエラーが発生しましたã€?" + outFile.getPath() + "]" ;
479                            throw new RuntimeException( errMsg,ex );
480                    }
481                    finally {
482                            Closer.ioClose( writer );
483                    }
484            }
485    
486            /**
487             * insert がã?"CHANGE" の場合ã?処ç�?µ�果を求めますã?
488             * 変換しなかったå?合ã?、null を返しますã?
489             * これは、変換カウントを算å?する為のフラグ代わりに使用してã�?�¾すã?
490             *
491             * @param       mach    キーワードã?正規表現
492             *
493             * @return      変換結果(対象行で無ã�??合ã?、null)
494             */
495            private String strChange( final Matcher mach ) {
496                    String line = null;
497                    if( mach.find() ) {
498                            line = mach.replaceAll( change );
499                    }
500                    return line ;
501            }
502    
503            /**
504             * insert がã?"BEFORE" の場合ã?処ç�?µ�果を求めますã?
505             * 変換しなかったå?合ã?、null を返しますã?
506             * これは、変換カウントを算å?する為のフラグ代わりに使用してã�?�¾すã?
507             *
508             * @param       line    検索è¡?
509             * @param       mach    キーワードã?正規表現
510             *
511             * @return      変換結果(対象行で無ã�??合ã?、null)
512             */
513            private String strBefore( final String line , final Matcher mach ) {
514                    boolean isChng = false;
515                    StringBuilder buf = new StringBuilder( line.length() );
516                    int indx = 0;
517                    while( mach.find() ) {
518                            isChng = true;
519                            int strt = mach.start() + insOffset;
520                            buf.append( line.substring( indx,strt ) );
521                            buf.append( change );
522                            indx = strt;
523                    }
524    
525                    String rtn = null;
526                    if( isChng ) {
527                            buf.append( line.substring( indx ) );
528                            rtn = buf.toString();
529                    }
530    
531                    return rtn ;
532            }
533    
534            /**
535             * insert がã?"AFTER" の場合ã?処ç�?µ�果を求めますã?
536             * 変換しなかったå?合ã?、null を返しますã?
537             * これは、変換カウントを算å?する為のフラグ代わりに使用してã�?�¾すã?
538             *
539             * @param       line    検索è¡?
540             * @param       mach    キーワードã?正規表現
541             *
542             * @return      変換結果(対象行で無ã�??合ã?、null)
543             */
544            private String strAfter( final String line , final Matcher mach ) {
545                    boolean isChng = false;
546                    StringBuilder buf = new StringBuilder( line.length() );
547                    int indx = 0;
548                    while( mach.find() ) {
549                            isChng = true;
550                            int end = mach.end() + insOffset;
551                            buf.append( line.substring( indx,end ) );
552                            buf.append( change );
553                            indx = end;
554                    }
555                    String rtn = null;
556                    if( isChng ) {
557                            buf.append( line.substring( indx ) );
558                            rtn = buf.toString();
559                    }
560    
561                    return rtn ;
562            }
563    
564            /**
565             * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
566             * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
567             * こã?æ–?­—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
568             * 形式で出してくださいã€?
569             *
570             * @return   処ç�?µ�果のレポã?ãƒ?
571             */
572            public String report() {
573                    if( findCount < cngCount ) { findCount = cngCount; }
574    
575                    String report = "[" + getClass().getName() + "]" + CR
576                                    + TAB + "Search Keyword    : " + keyword    + CR
577                                    + TAB + "Search File Count : " + inCount    + CR
578                                    + TAB + "Key Find    Count : " + findCount  + CR
579                                    + TAB + "Key Change  Count : " + cngCount ;
580    
581                    return report ;
582            }
583    
584            /**
585             * こã?クラスの使用方法を返しますã?
586             *
587             * @return      こã?クラスの使用方æ³?
588             */
589            public String usage() {
590                    StringBuilder buf = new StringBuilder();
591    
592                    buf.append( "Process_Grep は、上流から受け取っã�?FileLineModelから、文字å?を見つけå?ã�?           ).append( CR );
593                    buf.append( "ChainProcess インターフェースの実è£?‚¯ラスですã?"                                                              ).append( CR );
594                    buf.append( CR );
595                    buf.append( "正規表現の keyword を上流から受け取っã�?FileLineModel から検索しますã?"           ).append( CR );
596                    buf.append( "見つかった対象ファイルから、指定ã?æ–?­—å?を置換するå?合ã?ã€?change ã�?                  ).append( CR );
597                    buf.append( "-changeFile で、keyword を置換する文字å?を指定して下さã�??"                                        ).append( CR );
598                    buf.append( "置換する文字å?には、\t と \n の特殊文字が使用できますã?"                                           ).append( CR );
599                    buf.append( CR );
600                    buf.append( "処ç�?¯¾象はã€??常はã€?¼‘行づつ読み取りながら処ç�?‚’行いますã?存在チェãƒ?‚¯の場合ã?ã€?      ).append( CR );
601                    buf.append( "見つかった時点で処ç�?‚’中止しますã?これは、該当ç®?‰€をピãƒ?‚¯アãƒ??するのではなくã?"       ).append( CR );
602                    buf.append( "存在してã�?‚‹かどã�?�‹を判断して、あれã?、下流に流すとã�?�†のが目çš?� からですã?"              ).append( CR );
603                    buf.append( "keyword をã?改行を含ã‚?­£規表現で、検索・置換するå?合ã?ã€?useBulkRead 属æ?ã‚?               ).append( CR );
604                    buf.append( "true に設定してください。これã?、å?力ファイルをä¸?‹¬して読み込みますã?"                       ).append( CR );
605                    buf.append( "-ignoreCase は、検索時にキーの大æ–?­—小文字を無視するよã�?�«æŒ?®šしますã?"            ).append( CR );
606                    buf.append( "-notEquals は、結果(見つかればtrue)を反転(見つからなければtrue)しますã?"            ).append( CR );
607                    buf.append( "これは、行単位ではなくã?ファイル単位に判定しますã?で、change æŒ?®šしたå?å�?             ).append( CR );
608                    buf.append( "でもã?対象行ã?、見つかった行ですã?ただしã?下流に対して、見つからなã�?                   ).append( CR );
609                    buf.append( "場合だけå?ç�?‚’継続させますã?"                                                                                                    ).append( CR );
610                    buf.append( "-inEncode は、å?力ファイルのエンコード指定になりますã?"                                          ).append( CR );
611                    buf.append( "-outEncode は、å?力ファイルのエンコードや、changeFileでæŒ?®šã?置換文字å?"          ).append( CR );
612                    buf.append( "ファイルのエンコード指定になりますã?(changeFile は、å¿?�š 出力ファイルと)"                 ).append( CR );
613                    buf.append( "同じエンコードですã?"                                                                                                                 ).append( CR );
614                    buf.append( "これらã?エンコードが無æŒ?®šã?場合ã?、System.getProperty(\"file.encoding\") "      ).append( CR );
615                    buf.append( "で求まるå?を使用しますã?"                                                                                                              ).append( CR );
616                    buf.append( "-changeFile を使用することで、è¤?•°行ã?æ–?­—å?に置換することが可能ですã?"             ).append( CR );
617                    buf.append( CR );
618                    buf.append( "上æµ?プロセスチェインのãƒ??タは上流から渡されますã?)からのLineModel の"            ).append( CR );
619                    buf.append( "ファイルオブジェクトよりã?æŒ?®šã?æ–?­—å?が含まれてã�?‚‹か検索しますã?"                   ).append( CR );
620                    buf.append( "上流ã?ロセスでは、Name 属æ?として、ã?File』を持ち、å?は、Fileオブジェクãƒ?            ).append( CR );
621                    buf.append( "である、Process_FileSearch を使用するのがã?便利ですã?それ以外ã?クラスã‚?           ).append( CR );
622                    buf.append( "使用する場合でもã?Name属æ?と、File オブジェクトを持つ LineModel を受け渡ã�?  ).append( CR );
623                    buf.append( "できれば、使用可能ですã?"                                                                                                            ).append( CR );
624                    buf.append( CR );
625                    buf.append( "引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??" ).append( CR );
626                    buf.append( "引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に"             ).append( CR );
627                    buf.append( "繋げてくださいã€?                                                                                                                              ).append( CR );
628                    buf.append( CR ).append( CR );
629    
630                    buf.append( getArgument().usage() ).append( CR );
631    
632                    return buf.toString();
633            }
634    
635            /**
636             * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
637             *
638             * @param       args    コマンド引数配å?
639             */
640            public static void main( final String[] args ) {
641                    LogWriter.log( new Process_Grep().usage() );
642            }
643    }