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             * @og.rev 5.7.2.2 (2014/01/24) エラー時に??タも?力します?
195             *
196             * @param       data    オリジナルのLineModel
197             *
198             * @return      処?換後?LineModel
199             */
200            public LineModel action( final LineModel data ) {
201                    inCount++ ;
202                    final FileLineModel fileData ;
203                    if( data instanceof FileLineModel ) {
204                            fileData = (FileLineModel)data ;
205                    }
206                    else {
207                            String errMsg = "??タ?FileLineModel オブジェクトではありません? + CR ;
208                            throw new RuntimeException( errMsg );
209                    }
210    
211                    File org = fileData.getFile() ;
212                    String orgName = org.getPath();
213                    if( ! org.isFile() ) { return data; }
214    
215                    BufferedReader reader = FileUtil.getBufferedReader( org,inEncode );
216    //              File            tempFile  = new File( org.getPath() + "_temp" );
217    //              PrintWriter     tempWrt   = FileUtil.getPrintWriter( tempFile,outEncode );
218                    File            tempFile  = null;
219                    PrintWriter     tempWrt   = null;
220    
221                    // 5.1.2.0 (2010/01/01) 置換する?合?前??
222                    if( isChange ) {
223                            tempFile  = new File( orgName + "_temp" );
224                            tempWrt   = FileUtil.getPrintWriter( tempFile,outEncode );
225                    }
226    
227                    boolean nextFlag  = false;
228    
229                    try {
230                            String line ;
231                            int    lineNo = 0;
232                            while((line = reader.readLine()) != null) {
233                                    lineNo++ ;
234                                    StringBuilder buf = new StringBuilder( line );
235    //                              boolean foundFlag = false;              // 行単位に初期化する?
236                                    for( int i=0; i<keyword.length; i++ ) {
237                                            int indx = buf.indexOf( keyword[i] );
238                                            // 置換対象発見?行?力用に見つかれば、true にする?
239                                            if( indx >= 0 ) {
240    //                                              foundFlag = true;
241                                                    nextFlag  = true;               // ?度でも見つかれば、true にセ?
242                                                    if( display ) { println( orgName + ":" + lineNo + ":" + keyword[i] + ":" + line ); }
243                                            }
244                                            // 置換対象が見つかっても?isChange=true でなければ、置換???行わな??
245                                            if( isChange ) {
246                                                    while( indx >= 0 ) {
247                                                            buf.replace( indx,indx+keyword[i].length(),change[i] );
248                                                            indx = buf.indexOf( keyword[i],indx+change[i].length() );
249            //                                              nextFlag = true;                        // キーワードが存在したファイル?
250                                                            cngCount++ ;
251                                                            findCount++ ;
252                                                    }
253                                            }
254                                    }
255                                    // 5.1.2.0 (2010/01/01) 置換する?合?処?
256                                    if( isChange ) {
257                                            tempWrt.println( buf.toString() );
258                                    }
259                            }
260                    }
261                    catch ( IOException ex ) {
262                            String errMsg = "処?にエラーが発生しました?" + data.getRowNo() + "]件目" + CR
263    //                                              + data.toString() ;
264                                                    +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時に??タも?力します?
265                            throw new RuntimeException( errMsg,ex );
266                    }
267                    finally {
268                            Closer.ioClose( reader );
269                            Closer.ioClose( tempWrt );
270                    }
271    
272                    // 5.1.2.0 (2010/01/01) 置換する?合?処?
273                    if( isChange ) {
274                            if( nextFlag ) {
275                                    if( !org.delete() ) {
276                                            String errMsg = "??ファイルを削除できませんでした?" + org + "]" + CR
277                                                            +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時に??タも?力します?
278                                            throw new RuntimeException( errMsg );
279                                    }
280                                    if( !tempFile.renameTo( org ) ) {
281                                            String errMsg = "??ファイルをリネ??きませんでした?" + tempFile + "]" + CR
282                                                            +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時に??タも?力します?
283                                            throw new RuntimeException( errMsg );
284                                    }
285                            }
286                            else {
287                                    if( !tempFile.delete() ) {
288                                            String errMsg = "??ファイルを削除できませんでした?" + tempFile + "]" + CR
289                                                            +       "data=[" + data.dataLine() + "]" + CR ;         // 5.7.2.2 (2014/01/24) エラー時に??タも?力します?
290                                            throw new RuntimeException( errMsg );
291                                    }
292                            }
293                    }
294    
295                    return (nextFlag) ? data : null ;
296            }
297    
298            /**
299             * プロセスの処?果のレポ?ト表現を返します?
300             * 処??ログラ?、?力件数、?力件数などの??です?
301             * こ???をそのまま、標準?力に出すことで、結果レポ?トと出来るよ?
302             * 形式で出してください?
303             *
304             * @return   処?果のレポ??
305             */
306            public String report() {
307                    String report = "[" + getClass().getName() + "]" + CR
308                                    + TAB + "Search File Count : " + inCount    + CR
309                                    + TAB + "Key Find    Count : " + findCount  + CR
310                                    + TAB + "Key Change  Count : " + cngCount ;
311    
312                    return report ;
313            }
314    
315            /**
316             * こ?クラスの使用方法を返します?
317             *
318             * @return      こ?クラスの使用方?
319             */
320            public String usage() {
321                    StringBuilder buf = new StringBuilder();
322    
323                    buf.append( "Process_GrepChange は、上流から受け取っ?FileLineModelから、語句?                      ).append( CR );
324                    buf.append( "置換する?ChainProcess インターフェースの実?ラスです?"                                              ).append( CR );
325                    buf.append( "Process_Grep との違いは、チェ?するファイルのコピ??キーワードが存在"           ).append( CR );
326                    buf.append( "しなくと?作?することと、検索キーに正規表現が使えな???行置き換えが"            ).append( CR );
327                    buf.append( "出来な?とです?"                                                                                                                  ).append( CR );
328                    buf.append( CR );
329                    buf.append( "keywordFile より、置換する語句を含?ーと値のペアー(タブ区?)を読取り?  ).append( CR );
330                    buf.append( "対象とする語句を置換します?"                                                                                                  ).append( CR );
331                    buf.append( "keywordFile に、タブが含まれな?や、?頭にタブが存在して?場合??               ).append( CR );
332                    buf.append( "そ?行を読み飛?します?また?区?タブ?何?存在しても構いません?                    ).append( CR );
333                    buf.append( "ただし?タブで区?た前(キー)と後ろ(値)は、trim() されます?で、スペ?ス"            ).append( CR );
334                    buf.append( "が前後に存在して?場合?、ご注意く???"                                                                        ).append( CR );
335                    buf.append( "置換文?値)は、\t と \n の特殊文字が使用できます?"                                                      ).append( CR );
336                    buf.append( "こ? GrepChange では、語句に、正規表現は使用できません。正規表現のキーワー?   ).append( CR );
337                    buf.append( "?字?を?行???と置き換える場合?、Process_Grep を使用して下さ??"   ).append( CR );
338                    buf.append( "こ?プログラ?は、上流から受け取っ?FileLineModel のファイルに対して?           ).append( CR );
339                    buf.append( "置き換えた結果も?同じファイルにセーブします?"                                                            ).append( CR );
340                    buf.append( "??ファイルを保存したい場合?、予めバックア??を取得しておいてください?      ).append( CR );
341                    buf.append( "-inEncode は、?力ファイルのエンコード指定になります?"                                          ).append( CR );
342                    buf.append( "-outEncode は、?力ファイルのエンコードや、キーワードファイルのエンコー?       ).append( CR );
343                    buf.append( "?になります?(keywordFile は、? 出力ファイルと同じエンコードです?)"         ).append( CR );
344                    buf.append( "これら?エンコードが無??場合?、System.getProperty(\"file.encoding\") "      ).append( CR );
345                    buf.append( "で求まる?を使用します?"                                                                                                              ).append( CR );
346                    buf.append( CR );
347                    buf.append( "上流?ロセスでは、Name 属?として、?File』を持ち、?は、Fileオブジェク?            ).append( CR );
348                    buf.append( "である、Process_FileSearch を使用するのが?便利です?それ以外?クラス?           ).append( CR );
349                    buf.append( "使用する場合でも?Name属?と、File オブジェクトを持つ LineModel を受け渡?  ).append( CR );
350                    buf.append( "できれば、使用可能です?"                                                                                                            ).append( CR );
351                    buf.append( CR );
352                    buf.append( "引数??中に空白を含??合?、ダブルコー??ション(\"\") で括って下さ??" ).append( CR );
353                    buf.append( "引数??の ?』?前後には、空白は挟めません。??key=value の様に"             ).append( CR );
354                    buf.append( "繋げてください?                                                                                                                              ).append( CR );
355                    buf.append( CR ).append( CR );
356    
357                    buf.append( getArgument().usage() ).append( CR );
358    
359                    return buf.toString();
360            }
361    
362            /**
363             * こ?クラスは、main メソ?から実行できません?
364             *
365             * @param       args    コマンド引数配?
366             */
367            public static void main( final String[] args ) {
368                    LogWriter.log( new Process_GrepChange().usage() );
369            }
370    }