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.Map ;
026    import java.util.LinkedHashMap ;
027    import java.util.List ;
028    import java.util.ArrayList ;
029    
030    import java.io.File;
031    import java.io.PrintWriter;
032    import java.io.BufferedReader;
033    import java.io.IOException;
034    
035    /**
036     * Process_GrepChange は、上流から受け取っã�?FileLineModelから、語句ã‚?
037     * 置換するã?ChainProcess インターフェースの実è£?‚¯ラスですã?
038     *
039     * Process_Grep との違いは、チェãƒ?‚¯するファイルのコピã?ã‚?キーワードが存在
040     * しなくとã‚?作æ?することと、検索キーに正規表現が使えなã�??è¤?•°行置き換えが
041     * 出来なã�?�“とですã?
042     *
043     * keywordFile より、置換する語句を含ã‚?‚­ーと値のペアー(タブ区åˆ?‚Š)を読取りã€?
044     * 対象とする語句を置換しますã?
045     * keywordFile に、タブが含まれなã�?¡Œや、å?頭にタブが存在してã�?‚‹場合ã?ã€?
046     * そã?行を読み飛ã?しますã?またã?区åˆ?‚Šタブã?何å?存在しても構いませんã€?
047     * ただしã?タブで区åˆ?�£た前(キー)と後ろ(値)は、trim() されますã?で、スペã?ス
048     * が前後に存在してã�?‚‹場合ã?、ご注意くã�?�•ã�??
049     * 置換文å­?値)は、\t と \n の特殊文字が使用できますã?
050     * こã? GrepChange では、語句に、正規表現は使用できません。正規表現のキーワーãƒ?
051     * ã‚?–‡字å?をè¤?•°行ã?æ–?­—å?と置き換える場合ã?、Process_Grep を使用してくださいã€?
052     * こã?プログラãƒ?�§は、上流から受け取っã�?FileLineModel のファイルに対してã€?
053     * 置き換えた結果もã?同じファイルにセーブしますã?
054     * å…??ファイルを保存したい場合ã?、予めバックアãƒ??を取得しておいてくださいã€?
055     * -inEncode は、å?力ファイルのエンコード指定になりますã?
056     * -outEncode は、å?力ファイルのエンコードや、キーワードファイルの
057     * エンコード指定になりますã?(keywordFile は、å¿?�š 出力ファイルと同じエンコードですã?)
058     * これらã?エンコードが無æŒ?®šã?場合ã?、System.getProperty("file.encoding") で
059     * 求まるå?を使用しますã?
060     *
061     * 上流ã?ロセスでは、Name 属æ?として、ã?File』を持ち、å?は、Fileオブジェクãƒ?
062     * である、Process_FileSearch を使用するのがã?便利ですã?それ以外ã?クラスã‚?
063     * 使用する場合でもã?Name属æ?と、File オブジェクトを持つ LineModel を受け渡ã�?
064     * できれば、使用可能ですã?
065     *
066     * 引数æ–?­—å?中にスペã?スを含ã‚??合ã?、ダブルコーãƒ??ション("") で括って下さã�??
067     * 引数æ–?­—å?の ã€?』ã?前後には、スペã?スは挟めません。å¿?�šã€?key=value の様に
068     * 繋げてくださいã€?
069     *
070     *  Process_GrepChange -keyword=検索æ–?­—å? -ignoreCase=true -outfile=OUTFILE -encode=UTF-8
071     *
072     *    -keywordFile=キーワーãƒ?   ?š置換する語句を含ã‚?‚­ーと値のペアー(タブ区åˆ?‚Š)
073     *   [-ignoreCase=大æ–?­—小文å­?] ?š検索時に大æ–?­—小文字を区別しなã�?true)かどã�?�‹(初期値:false[区別する])
074     *   [-isChange=置換可否       ] ?š置換å?ç�?‚’実施する(true)かどã�?�‹(初期値:置換する[true])
075     *   [-inEncode=入力エンコーãƒ?] ?šå?力ファイルのエンコードタイãƒ?
076     *   [-outEncode=出力エンコード] ?šå?力ファイルã‚?‚­ーワードファイルのエンコードタイãƒ?
077     *   [-display=false|true      ] ?š結果を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
078     *
079     * @version  4.0
080     * @author   Kazuhiko Hasegawa
081     * @since    JDK5.0,
082     */
083    public class Process_GrepChange extends AbstractProcess implements ChainProcess {
084            private String[]        keyword = null;
085            private String[]        change  = null;
086            private boolean         ignoreCase      = false;
087            private boolean         isChange        = true;         // 5.1.2.0 (2010/01/01) 置換するかどã�?�‹を指定可能にする
088            private String          inEncode        = null;
089            private String          outEncode       = null;
090            private boolean         display         = false;        // 表示しなã�?
091    
092            private int             inCount         = 0;
093            private int             findCount       = 0;
094            private int             cngCount        = 0;
095    
096            private static final Map<String,String> mustProparty   ;          // ?»プロパティ?½å¿??チェãƒ?‚¯用 Map
097            private static final Map<String,String> usableProparty ;          // ?»プロパティ?½整合æ?チェãƒ?‚¯ Map
098    
099            static {
100                    mustProparty = new LinkedHashMap<String,String>();
101                    mustProparty.put( "keywordFile",        "置換する語句を含ã‚?‚­ーと値のペアー(タブ区åˆ?‚Š)(å¿??)" );
102    
103                    usableProparty = new LinkedHashMap<String,String>();
104                    usableProparty.put( "ignoreCase",       "検索時に大æ–?­—小文字を区別しなã�?true)かどã�?�‹ã€? +
105                                                                                    CR + "(初期値:区別する[false])" );
106                    usableProparty.put( "isChange",         "置換å?ç�?‚’実施する(true)かどã�?�‹" +
107                                                                                    CR + "(初期値:置換する[true])" );
108                    usableProparty.put( "inEncode",         "入力ファイルのエンコードタイãƒ? );
109                    usableProparty.put( "outEncode",        "出力ファイルã‚?‚­ーワードファイルのエンコードタイãƒ? );
110                    usableProparty.put( "display",          "結果を標準å?力に表示する(true)かしなã�?false)ã�? +
111                                                                                    CR + "(初期値:false:表示しなã�?" );
112            }
113    
114            /**
115             * ãƒ?ƒ•ォルトコンストラクターã€?
116             * こã?クラスは、動çš?½œæ?されますã?ãƒ?ƒ•ォルトコンストラクターでã€?
117             * super クラスに対して、å¿?¦�な初期化を行っておきますã?
118             *
119             */
120            public Process_GrepChange() {
121                    super( "org.opengion.fukurou.process.Process_GrepChange",mustProparty,usableProparty );
122            }
123    
124            /**
125             * プロセスの初期化を行いますã?初めにä¸?º¦ã�?�‘、呼び出されますã?
126             * 初期処ç�?ファイルオープンã€?¼¤?¢オープンç­?に使用しますã?
127             *
128             * @og.rev 5.1.2.0 (2010/01/01) 置換するかどã�?�‹を指定可能にする(isChange)属æ?追åŠ?
129             *
130             * @param   paramProcess ãƒ??タベã?スの接続å?æƒ??などを持ってã�?‚‹オブジェクãƒ?
131             */
132            public void init( final ParamProcess paramProcess ) {
133                    Argument arg = getArgument();
134    
135                    String keywordFile = arg.getProparty("keywordFile" );
136                    ignoreCase              = arg.getProparty("ignoreCase",ignoreCase);
137                    isChange                = arg.getProparty("isChange",isChange);                 // 5.1.2.0 (2010/01/01)
138                    inEncode                = arg.getProparty("inEncode",System.getProperty("file.encoding"));
139                    outEncode               = arg.getProparty("outEncode",System.getProperty("file.encoding"));
140                    display                 = arg.getProparty("display",display);
141    
142                    FileString fs = new FileString();
143                    fs.setFilename( keywordFile );
144                    fs.setEncode( outEncode );
145                    String[] lines = fs.getValue( "\n" );
146                    int len = lines.length;
147                    if( len == 0 ) {
148                            String errMsg = "keywordFile のå†?®¹ã�?読み取れませんでしたã€?" + keywordFile + "]" ;
149                            throw new RuntimeException( errMsg );
150                    }
151    
152                    println( "keywordFile をã?" + len + "件読み取りましたã€? );
153                    List<String> keyList = new ArrayList<String>( len );
154                    List<String> cngList = new ArrayList<String>( len );
155    
156                    for( int i=0; i<len; i++ ) {
157            //              String line = lines[i].trim();
158                            String line = lines[i];
159                            int indx = line.indexOf( '\t' );
160                            if( indx <= 0 ) { continue ; }       // TAB がå?頭ã‚??存在しなã�?¡Œã?読み飛ã?すã?
161                            keyList.add( line.substring( 0,indx ).trim() );
162                            String cng = line.substring( indx+1 ).trim();
163                            cng = StringUtil.replace( cng,"\\n",CR );
164                            cng = StringUtil.replace( cng,"\\t","\t" );
165                            cngList.add( cng );
166                    }
167                    keyword = keyList.toArray( new String[keyList.size()] );
168                    change  = cngList.toArray( new String[cngList.size()] );
169            }
170    
171            /**
172             * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
173             * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç­?に使用しますã?
174             *
175             * @param   isOK トã?タルで、OKã�?�£たかどã�?�‹[true:成功/false:失敗]
176             */
177            public void end( final boolean isOK ) {
178                    // ここでは処ç�?‚’行いませんã€?
179            }
180    
181            /**
182             * 引数の LineModel をå?ç�?�™るメソãƒ?ƒ‰ですã?
183             * 変換処ç�?¾Œã? LineModel を返しますã?
184             * 後続å?ç�?‚’行わなã�??å�?ãƒ??タのフィルタリングを行う場å�?はã€?
185             * null ãƒ??タを返しますã?つまりã?null ãƒ??タは、後続å?ç�?‚’行わなã�?
186             * フラグの代わりにも使用してã�?�¾すã?
187             * なおã?変換処ç�?¾Œã? LineModel と、オリジナルの LineModel がã?
188             * 同ä¸?�‹、コピã?(クローン)かã?、各処ç�?ƒ¡ソãƒ?ƒ‰å†?�§決めてã�?�¾すã?
189             * ドキュメントに明記されてã�?�ªã�??合ã?、副作用が問題になるå?合ã?ã€?
190             * å�??ç�?�”とに自åˆ?�§コピã?(クローン)して下さã�??
191             *
192             * @og.rev 4.0.0.0 (2007/11/28) メソãƒ?ƒ‰の戻りå?をチェãƒ?‚¯しますã?
193             * @og.rev 5.1.2.0 (2010/01/01) 置換するかどã�?�‹を指定可能にする(isChange)属æ?追åŠ?
194             *
195             * @param       data    オリジナルのLineModel
196             *
197             * @return      処ç�?¤‰換後ã?LineModel
198             */
199            public LineModel action( final LineModel data ) {
200                    inCount++ ;
201                    final FileLineModel fileData ;
202                    if( data instanceof FileLineModel ) {
203                            fileData = (FileLineModel)data ;
204                    }
205                    else {
206                            String errMsg = "ãƒ??タã�?FileLineModel オブジェクトではありませんã€? + CR ;
207                            throw new RuntimeException( errMsg );
208                    }
209    
210                    File org = fileData.getFile() ;
211                    String orgName = org.getPath();
212                    if( ! org.isFile() ) { return data; }
213    
214                    BufferedReader reader = FileUtil.getBufferedReader( org,inEncode );
215    //              File            tempFile  = new File( org.getPath() + "_temp" );
216    //              PrintWriter     tempWrt   = FileUtil.getPrintWriter( tempFile,outEncode );
217                    File            tempFile  = null;
218                    PrintWriter     tempWrt   = null;
219    
220                    // 5.1.2.0 (2010/01/01) 置換するå?合ã?前å?ç�?
221                    if( isChange ) {
222                            tempFile  = new File( orgName + "_temp" );
223                            tempWrt   = FileUtil.getPrintWriter( tempFile,outEncode );
224                    }
225    
226                    boolean nextFlag  = false;
227    
228                    try {
229                            String line ;
230                            int    lineNo = 0;
231                            while((line = reader.readLine()) != null) {
232                                    lineNo++ ;
233                                    StringBuilder buf = new StringBuilder( line );
234    //                              boolean foundFlag = false;              // 行単位に初期化するã?
235                                    for( int i=0; i<keyword.length; i++ ) {
236                                            int indx = buf.indexOf( keyword[i] );
237                                            // 置換対象発見ã?行å?力用に見つかれば、true にするã€?
238                                            if( indx >= 0 ) {
239    //                                              foundFlag = true;
240                                                    nextFlag  = true;               // ?‘度でも見つかれば、true にセãƒ?ƒˆ
241                                                    if( display ) { println( orgName + ":" + lineNo + ":" + keyword[i] + ":" + line ); }
242                                            }
243                                            // 置換対象が見つかってもã?isChange=true でなければ、置換å?ç�??行わなã�??
244                                            if( isChange ) {
245                                                    while( indx >= 0 ) {
246                                                            buf.replace( indx,indx+keyword[i].length(),change[i] );
247                                                            indx = buf.indexOf( keyword[i],indx+change[i].length() );
248            //                                              nextFlag = true;                        // キーワードが存在したファイルã€?
249                                                            cngCount++ ;
250                                                            findCount++ ;
251                                                    }
252                                            }
253                                    }
254                                    // 5.1.2.0 (2010/01/01) 置換するå?合ã?処ç�?
255                                    if( isChange ) {
256                                            tempWrt.println( buf.toString() );
257                                    }
258                            }
259                    }
260                    catch ( IOException ex ) {
261                            String errMsg = "処ç�?¸­にエラーが発生しましたã€?" + data.getRowNo() + "]件目" + CR
262                                                    + data.toString() ;
263                            throw new RuntimeException( errMsg,ex );
264                    }
265                    finally {
266                            Closer.ioClose( reader );
267                            Closer.ioClose( tempWrt );
268                    }
269    
270                    // 5.1.2.0 (2010/01/01) 置換するå?合ã?処ç�?
271                    if( isChange ) {
272                            if( nextFlag ) {
273                                    if( !org.delete() ) {
274                                            String errMsg = "æ‰?®šã?ファイルを削除できませんでしたã€?" + org + "]" ;
275                                            throw new RuntimeException( errMsg );
276                                    }
277                                    if( !tempFile.renameTo( org ) ) {
278                                            String errMsg = "æ‰?®šã?ファイルをリネã?ãƒ?�§きませんでしたã€?" + tempFile + "]" ;
279                                            throw new RuntimeException( errMsg );
280                                    }
281                            }
282                            else {
283                                    if( !tempFile.delete() ) {
284                                            String errMsg = "æ‰?®šã?ファイルを削除できませんでしたã€?" + tempFile + "]" ;
285                                            throw new RuntimeException( errMsg );
286                                    }
287                            }
288                    }
289    
290                    return (nextFlag) ? data : null ;
291            }
292    
293            /**
294             * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
295             * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
296             * こã?æ–?­—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
297             * 形式で出してくださいã€?
298             *
299             * @return   処ç�?µ�果のレポã?ãƒ?
300             */
301            public String report() {
302                    String report = "[" + getClass().getName() + "]" + CR
303                                    + TAB + "Search File Count : " + inCount    + CR
304                                    + TAB + "Key Find    Count : " + findCount  + CR
305                                    + TAB + "Key Change  Count : " + cngCount ;
306    
307                    return report ;
308            }
309    
310            /**
311             * こã?クラスの使用方法を返しますã?
312             *
313             * @return      こã?クラスの使用方æ³?
314             */
315            public String usage() {
316                    StringBuilder buf = new StringBuilder();
317    
318                    buf.append( "Process_GrepChange は、上流から受け取っã�?FileLineModelから、語句ã‚?                      ).append( CR );
319                    buf.append( "置換するã?ChainProcess インターフェースの実è£?‚¯ラスですã?"                                              ).append( CR );
320                    buf.append( "Process_Grep との違いは、チェãƒ?‚¯するファイルのコピã?ã‚?キーワードが存在"           ).append( CR );
321                    buf.append( "しなくとã‚?作æ?することと、検索キーに正規表現が使えなã�??è¤?•°行置き換えが"            ).append( CR );
322                    buf.append( "出来なã�?�“とですã?"                                                                                                                  ).append( CR );
323                    buf.append( CR );
324                    buf.append( "keywordFile より、置換する語句を含ã‚?‚­ーと値のペアー(タブ区åˆ?‚Š)を読取りã€?  ).append( CR );
325                    buf.append( "対象とする語句を置換しますã?"                                                                                                  ).append( CR );
326                    buf.append( "keywordFile に、タブが含まれなã�?¡Œや、å?頭にタブが存在してã�?‚‹場合ã?ã€?               ).append( CR );
327                    buf.append( "そã?行を読み飛ã?しますã?またã?区åˆ?‚Šタブã?何å?存在しても構いませんã€?                    ).append( CR );
328                    buf.append( "ただしã?タブで区åˆ?�£た前(キー)と後ろ(値)は、trim() されますã?で、スペã?ス"            ).append( CR );
329                    buf.append( "が前後に存在してã�?‚‹場合ã?、ご注意くã�?�•ã�??"                                                                        ).append( CR );
330                    buf.append( "置換文å­?値)は、\t と \n の特殊文字が使用できますã?"                                                      ).append( CR );
331                    buf.append( "こã? GrepChange では、語句に、正規表現は使用できません。正規表現のキーワーãƒ?   ).append( CR );
332                    buf.append( "ã‚?–‡字å?をè¤?•°行ã?æ–?­—å?と置き換える場合ã?、Process_Grep を使用して下さã�??"   ).append( CR );
333                    buf.append( "こã?プログラãƒ?�§は、上流から受け取っã�?FileLineModel のファイルに対してã€?           ).append( CR );
334                    buf.append( "置き換えた結果もã?同じファイルにセーブしますã?"                                                            ).append( CR );
335                    buf.append( "å…??ファイルを保存したい場合ã?、予めバックアãƒ??を取得しておいてくださいã€?      ).append( CR );
336                    buf.append( "-inEncode は、å?力ファイルのエンコード指定になりますã?"                                          ).append( CR );
337                    buf.append( "-outEncode は、å?力ファイルのエンコードや、キーワードファイルのエンコーãƒ?       ).append( CR );
338                    buf.append( "æŒ?®šになりますã?(keywordFile は、å¿?�š 出力ファイルと同じエンコードですã?)"         ).append( CR );
339                    buf.append( "これらã?エンコードが無æŒ?®šã?場合ã?、System.getProperty(\"file.encoding\") "      ).append( CR );
340                    buf.append( "で求まるå?を使用しますã?"                                                                                                              ).append( CR );
341                    buf.append( CR );
342                    buf.append( "上流ã?ロセスでは、Name 属æ?として、ã?File』を持ち、å?は、Fileオブジェクãƒ?            ).append( CR );
343                    buf.append( "である、Process_FileSearch を使用するのがã?便利ですã?それ以外ã?クラスã‚?           ).append( CR );
344                    buf.append( "使用する場合でもã?Name属æ?と、File オブジェクトを持つ LineModel を受け渡ã�?  ).append( CR );
345                    buf.append( "できれば、使用可能ですã?"                                                                                                            ).append( CR );
346                    buf.append( CR );
347                    buf.append( "引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??" ).append( CR );
348                    buf.append( "引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に"             ).append( CR );
349                    buf.append( "繋げてくださいã€?                                                                                                                              ).append( CR );
350                    buf.append( CR ).append( CR );
351    
352                    buf.append( getArgument().usage() ).append( CR );
353    
354                    return buf.toString();
355            }
356    
357            /**
358             * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
359             *
360             * @param       args    コマンド引数配å?
361             */
362            public static void main( final String[] args ) {
363                    LogWriter.log( new Process_GrepChange().usage() );
364            }
365    }