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.StringUtil;
020    import org.opengion.fukurou.util.FileUtil;
021    import org.opengion.fukurou.util.Closer ;
022    import org.opengion.fukurou.util.LogWriter;
023    
024    import java.util.Map ;
025    import java.util.LinkedHashMap ;
026    
027    import java.io.File;
028    import java.io.BufferedReader;
029    import java.io.IOException;
030    
031    /**
032     * Process_TableReaderは、ファイルから読み取ったå?容をã?LineModel に設定後ã?
033     * 下流に渡すã?FirstProcess インターフェースの実è£?‚¯ラスですã?
034     *
035     * DBTableModel 形式ã?ファイルを読み取って、各行を LineModel にセãƒ?ƒˆしてã€?
036     * 下æµ?プロセスチェインのãƒ??タは上流から下流に渡されますã?)に渡しますã?
037     *
038     * columns 属æ?はã€?NAME で列カラãƒ?‚’外部からæŒ?®šするå?合に使用しますã?
039     * こã?属æ?とuseNumber属æ?は独立してã�?�¾すが、ä¸?ˆ¬にはã€?NAME を指å®?
040     * する場合ã?、useNumber="true"として、行番号æ¬??使用しますし、外部から
041     * æŒ?®šするå?合ã?、useNumber="false"にして先é?から読み取りますã?
042     * (自動セãƒ?ƒˆではなã�??で、å¿?¦�に応じて設定してください)
043     * useNumber の初期値はã€?true" ですã?
044     *
045     * ※ 注æ„?
046     *  Process_TableReader では、セパレータæ–?­?で区åˆ?�£て読み込ã‚??ç�?�§、前後ã??½?�゚ー?½ã‚?
047     *  削除してã�?�¾すã?
048     *
049     * 引数æ–?­—å?中にスペã?スを含ã‚??合ã?、ダブルコーãƒ??ション("") で括って下さã�??
050     * 引数æ–?­—å?の ã€?』ã?前後には、スペã?スは挟めません。å¿?�šã€?key=value の様に
051     * 繋げてくださいã€?
052     *
053     * @og.formSample
054     *  Process_TableReader -infile=INFILE -sep=, -encode=UTF-8 -columns=AA,BB,CC
055     *
056     *    -infile=入力ファイルå�?    ?šå?力ファイルå�?
057     *   [-existCheck=存在確èª?    ] ?šファイルが存在しなã�??合エラーにする(初期値:true)
058     *   [-sep=セパレータæ–?­?     ] ?š区åˆ?‚Šæ–?­?初期値:タãƒ?
059     *   [-encode=æ–?­—エンコーãƒ?  ] ?šå?力ファイルのエンコードタイãƒ?
060     *   [-columns=読み取りカラãƒ?��] ?šå?力カラãƒ?��(カンマ区åˆ?‚Š)
061     *   [-useNumber=[true/false]  ] ?š行番号を使用する(true)か使用しなã�?false)かã?
062     *   [-display=[false/true]    ] ?š結果を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
063     *   [-debug=[false/true]      ] ?šデバッグæƒ??を標準å?力に表示する(true)かしなã�?false)ã�?初期値:false[表示しない])
064     *
065     * @version  4.0
066     * @author   Kazuhiko Hasegawa
067     * @since    JDK5.0,
068     */
069    public class Process_TableReader extends AbstractProcess implements FirstProcess {
070            private String                  separator       = TAB;  // é ?›®区åˆ?‚Šæ–?­?
071            private String                  infile          = null;
072            private BufferedReader  reader          = null;
073            private LineModel               model           = null;
074            private String                  line            = null;
075            private int[]                   clmNos          = null;         // ファイルのヘッãƒ??のカラãƒ?•ª号
076            private boolean                 useNumber       = true;         // 5.2.2.0 (2010/11/01) 行番号を使用する(true)か使用しなã�?false)ã�?
077            private boolean                 nameNull        = false;        // ?�件ãƒ??タæ™?true
078            private boolean                 display         = false;        // 表示しなã�?
079            private boolean                 debug           = false;        // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
080    
081            private int                             inCount         = 0;
082            private int                             outCount        = 0;
083    
084            private static final Map<String,String> mustProparty   ;          // ?»プロパティ?½å¿??チェãƒ?‚¯用 Map
085            private static final Map<String,String> usableProparty ;          // ?»プロパティ?½整合æ?チェãƒ?‚¯ Map
086    
087            static {
088                    mustProparty = new LinkedHashMap<String,String>();
089                    mustProparty.put( "infile",     "入力ファイルå�?(å¿??)" );
090    
091                    usableProparty = new LinkedHashMap<String,String>();
092                    usableProparty.put( "existCheck",       "ファイルが存在しなã�??合エラーにする(初期値:true)" );
093                    usableProparty.put( "sep",                      "区åˆ?‚Šæ–?­?初期値:タãƒ?" );
094                    usableProparty.put( "encode",           "入力ファイルのエンコードタイãƒ? );
095                    usableProparty.put( "columns",          "入力カラãƒ?��(カンマ区åˆ?‚Š)" );
096                    usableProparty.put( "useNumber",        "行番号を使用する(true)か使用しなã�?false)ã�? );     // 5.2.2.0 (2010/11/01)
097                    usableProparty.put( "display",          "結果を標準å?力に表示する(true)かしなã�?false)ã�? +
098                                                                                            CR + " (初期値:false:表示しなã�?" );
099                    usableProparty.put( "debug",    "ãƒ?ƒ�ãƒ?‚°æƒ??を標準å?力に表示する(true)かしなã�?false)ã�? +
100                                                                                            CR + "(初期値:false:表示しなã�?" );                // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
101            }
102    
103            /**
104             * ãƒ?ƒ•ォルトコンストラクターã€?
105             * こã?クラスは、動çš?½œæ?されますã?ãƒ?ƒ•ォルトコンストラクターでã€?
106             * super クラスに対して、å¿?¦�な初期化を行っておきますã?
107             *
108             */
109            public Process_TableReader() {
110                    super( "org.opengion.fukurou.process.Process_TableReader",mustProparty,usableProparty );
111            }
112    
113            /**
114             * プロセスの初期化を行いますã?初めにä¸?º¦ã�?�‘、呼び出されますã?
115             * 初期処ç�?ファイルオープンã€?¼¤?¢オープンç­?に使用しますã?
116             *
117             * @og.rev 5.2.2.0 (2010/11/01) useNumber属æ?の追åŠ?
118             *
119             * @param   paramProcess ãƒ??タベã?スの接続å?æƒ??などを持ってã�?‚‹オブジェクãƒ?
120             */
121            public void init( final ParamProcess paramProcess ) {
122                    Argument arg = getArgument();
123    
124                    infile                          = arg.getProparty("infile");
125                    boolean existCheck      = arg.getProparty("existCheck",true);
126                    String  encode          = arg.getProparty("encode",System.getProperty("file.encoding"));
127                    separator                       = arg.getProparty("sep",separator );
128                    String  clms            = arg.getProparty("columns" );
129                    useNumber                       = arg.getProparty("useNumber",useNumber);       // 5.2.2.0 (2010/11/01)
130                    display                         = arg.getProparty("display",display);
131                    debug                           = arg.getProparty("debug",debug);                               // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
132    //              if( debug ) { println( arg.toString() ); }                      // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
133    
134                    if( infile == null ) {
135                            String errMsg = "ファイル名がæŒ?®šされてã�?�¾せんã€? ;
136                            throw new RuntimeException( errMsg );
137                    }
138    
139                    File file = new File( infile );
140    
141                    if( ! file.exists() ) {
142                            if( existCheck ) {
143                                    String errMsg = "ファイルが存在しませんã€?ile=[" + file + "]" ;
144                                    throw new RuntimeException( errMsg );
145                            }
146                            else {
147                                    nameNull = true; return ;
148                            }
149                    }
150    
151                    if( ! file.isFile() ) {
152                            String errMsg = "ファイル名をæŒ?®šしてくださいã€?ile=[" + file + "]" ;
153                            throw new RuntimeException( errMsg );
154                    }
155    
156                    reader = FileUtil.getBufferedReader( file,encode );
157    
158                    // 5.2.2.0 (2010/11/01) names の外部æŒ?®šã?処ç�?‚’先に行うã€?
159    //              String[] clmNames = readName( reader );         // ファイルのカラãƒ?��配å?
160    //              if( clmNames == null || clmNames.length == 0 ) { nameNull = true; return ; }
161    
162                    final String[] names ;
163                    if( clms != null ) {
164                            names = StringUtil.csv2Array( clms );   // æŒ?®šã?カラãƒ?��配å?
165                    }
166                    else {
167                            // 5.2.2.0 (2010/11/01) names の外部æŒ?®šã?処ç�?‚’先に行うã€?
168                            String[] clmNames = readName( reader );         // ファイルのカラãƒ?��配å?
169                            if( clmNames == null || clmNames.length == 0 ) { nameNull = true; return ; }
170                            names = clmNames;
171                    }
172    
173                    model = new LineModel();
174                    model.init( names );
175    
176                    if( display ) { println( model.nameLine() ); }
177    
178    //              clmNos = new int[names.length];
179    //              for( int i=0; i<clmNames.length; i++ ) {
180    //                      int no = model.getColumnNo( clmNames[i] );
181    //                      if( no >= 0 ) { clmNos[no] = i+1; }          // 行番号åˆ?‚’?‹1しておくã€?
182    //              }
183                    clmNos = new int[names.length];
184                    for( int i=0; i<names.length; i++ ) {
185                            int no = model.getColumnNo( names[i] );
186                            // 5.2.2.0 (2010/11/01) useNumber="true"の場合ã?、行番号åˆ?‚’?‹1しておくã€?
187                            if( no >= 0 ) { clmNos[no] = (useNumber) ? i+1 : i ; }
188                    }
189            }
190    
191            /**
192             * プロセスの終äº?‚’行いますã?æœ?¾Œにä¸?º¦ã�?�‘、呼び出されますã?
193             * 終äº??ç�?ファイルクローズã€?¼¤?¢クローズç­?に使用しますã?
194             *
195             * @param   isOK トã?タルで、OKã�?�£たかどã�?�‹[true:成功/false:失敗]
196             */
197            public void end( final boolean isOK ) {
198                    Closer.ioClose( reader );
199                    reader = null;
200            }
201    
202            /**
203             * こã?ãƒ??タの処ç�?�«おいて、次の処ç�?�Œ出来るかどã�?�‹を問ã�?�ˆわせますã?
204             * こã?呼び出し1回毎に、次のãƒ??タを取得する準備を行いますã?
205             *
206             * @og.rev 5.2.2.0 (2010/11/01) ""で囲われてã�?‚‹ãƒ??タに改行が入ってã�?�Ÿ場合ã?対å¿?
207             *
208             * @return      処ç�?�§きる:true / 処ç�?�§きなã�?false
209             */
210            public boolean next() {
211                    if( nameNull ) { return false; }
212    
213                    boolean flag = false;
214                    try {
215                            while((line = reader.readLine()) != null) {
216                                    inCount++ ;
217                                    if( line.length() == 0 || line.charAt( 0 ) == '#' ) { continue; }
218                                    else {
219                                            // 5.2.2.0 (2010/11/01) findbugs 対ç­?æ–?­—å?の + 連結と、å¥?•°判定ロジãƒ?‚¯)
220                                            int quotCount = StringUtil.countChar( line, '"' );
221                                            if( quotCount % 2 != 0 ) {
222                                                    String addLine = null;
223                                                    StringBuilder buf = new StringBuilder( line );
224                                                    while(quotCount % 2 != 0 && (addLine = reader.readLine()) != null) {
225                                                            if( addLine.length() == 0 || addLine.charAt( 0 ) == '#' ) { continue; }
226                                                            buf.append( CR ).append( addLine );
227                                                            quotCount += StringUtil.countChar( addLine, '"' );
228                                                    }
229                                                    line = buf.toString();
230                                            }
231                                            flag = true;
232                                            break;
233                                    }
234                            }
235                    }
236                    catch (IOException ex) {
237                            String errMsg = "ファイル読込みエラー[" + reader.toString() + "]"  ;
238                            throw new RuntimeException( errMsg,ex );
239                    }
240                    if( debug ) { println( line ); }                        // 5.7.3.0 (2014/02/07) ãƒ?ƒ�ãƒ?‚°æƒ??
241                    return flag;
242            }
243    
244            /**
245             * æœ??にã€?行データである LineModel を作æ?しまã�?
246             * FirstProcess は、次ã€?�¨処ç�?‚’チェインしてã�?��æœ??の行データã‚?
247             * 作æ?して、後続ã? ChainProcess クラスに処ç�?ƒ‡ータを渡しますã?
248             *
249             * ファイルより読み込んだ?‘行ã?ãƒ??タã‚?ãƒ??ブルモãƒ?ƒ«に
250             * セãƒ?ƒˆするようにåˆ?‰²しまã�?
251             * なおã?読込みは?ŒNAMEé ?›®åˆ?‚’読み込みますã?ãƒ??タ件数が少なã�??合ã?ã€?
252             * "" をセãƒ?ƒˆしておきますã?
253             *
254             * @param       rowNo   処ç�?¸­の行番号
255             *
256             * @return      処ç�?¤‰換後ã?LineModel
257             */
258            public LineModel makeLineModel( final int rowNo ) {
259                    outCount++ ;
260                    String[] vals = StringUtil.csv2Array( line ,separator.charAt(0) );
261    
262                    int len = vals.length;
263                    for( int clmNo=0; clmNo<model.size(); clmNo++ ) {
264                            int no = clmNos[clmNo];
265                            if( len > no ) {
266                                    model.setValue( clmNo,vals[no] );
267                            }
268                            else {
269                                    // EXCEL がã?終端TABを削除してしまã�?�Ÿめã?少なã�??合ã?埋めるã?
270                                    model.setValue( clmNo,"" );
271                            }
272                    }
273                    model.setRowNo( rowNo ) ;
274    
275                    if( display ) { println( model.dataLine() ); }
276    
277                    return model;
278            }
279    
280            /**
281             * BufferedReader よりã€?NAME 行ã?é ?›®名情報を読み取りますã?
282             * ãƒ??タカラãƒ?‚ˆり前にã€??目名情報を示ã�?"#Name" が存在する仮定で取り込みますã?
283             * こã?行ã?、ファイルの形式に無関係に、TAB で区åˆ?‚‰れてã�?�¾すã?
284             *
285             * @param       reader PrintWriterオブジェクãƒ?
286             *
287             * @return      カラãƒ?��配å?(存在しなã�??合ã?、サイズ?�ã?配å?)
288             */
289            private String[] readName( final BufferedReader reader ) {
290                    try {
291                            // 4.0.0 (2005/01/31) line 変数名変更
292                            String line1;
293                            while((line1 = reader.readLine()) != null) {
294                                    inCount++ ;
295                                    if( line1.length() == 0 ) { continue; }
296                                    if( line1.charAt(0) == '#' ) {
297                                            String key = line1.substring( 0,5 );
298                                            if( key.equalsIgnoreCase( "#NAME" ) ) {
299                                                    // è¶?‚¤レギュラー処ç�?æœ??の TAB 以前ã?æ–?­—ã?無視するã?
300                                                    String line2 = line1.substring( line1.indexOf( TAB )+1 );
301                                                    return StringUtil.csv2Array( line2 ,TAB.charAt(0) );
302                                            }
303                                            else  { continue; }
304                                    }
305                                    else {
306                                            String errMsg = "#NAME が見つかる前にãƒ??タが見つかりましたã€?;
307                                            throw new RuntimeException( errMsg );
308                                    }
309                            }
310                    }
311                    catch (IOException ex) {
312                            String errMsg = "ファイル読込みエラー[" + reader.toString() + "]"  ;
313                            throw new RuntimeException( errMsg,ex );
314                    }
315                    return new String[0];
316            }
317    
318            /**
319             * プロセスの処ç�?µ�果のレポã?ト表現を返しますã?
320             * 処ç�??ログラãƒ?��、å?力件数、å?力件数などのæƒ??ですã?
321             * こã?æ–?­—å?をそのまま、標準å?力に出すことで、結果レポã?トと出来るよã�?�ª
322             * 形式で出してくださいã€?
323             *
324             * @return   処ç�?µ�果のレポã?ãƒ?
325             */
326            public String report() {
327                    String report = "[" + getClass().getName() + "]" + CR
328                                    + TAB + "Input  File  : " + infile      + CR
329                                    + TAB + "Input  Count : " + inCount     + CR
330                                    + TAB + "Output Count : " + outCount ;
331    
332                    return report ;
333            }
334    
335            /**
336             * こã?クラスの使用方法を返しますã?
337             *
338             * @og.rev 5.2.2.0 (2010/11/01) useNumber属æ?のコメント追åŠ?
339             *
340             * @return      こã?クラスの使用方æ³?
341             */
342            public String usage() {
343                    StringBuilder buf = new StringBuilder();
344    
345                    buf.append( "Process_TableReaderは、ファイルから読み取ったå?容をã?LineModel に設定後ã?"       ).append( CR );
346                    buf.append( "下流に渡すã?FirstProcess インターフェースの実è£?‚¯ラスですã?"                                    ).append( CR );
347                    buf.append( CR );
348                    buf.append( "DBTableModel 形式ã?ファイルを読み取って、各行を LineModel にセãƒ?ƒˆしてã€?          ).append( CR );
349                    buf.append( "下æµ?プロセスチェインのãƒ??タは上流から下流に渡されますã?)に渡しますã?"              ).append( CR );
350                    buf.append( CR );
351                    buf.append( "columns 属æ?はã€?NAME で列カラãƒ?‚’外部からæŒ?®šするå?合に使用しますã?"                 ).append( CR );
352                    buf.append( "こã?属æ?とuseNumber属æ?は独立してã�?�¾すが、ä¸?ˆ¬にはã€?NAME を指å®?                           ).append( CR );
353                    buf.append( "する場合ã?、useNumber=\"true\"として、行番号æ¬??使用しますし、外部から"              ).append( CR );
354                    buf.append( "æŒ?®šするå?合ã?、useNumber=\"false\"にして先é?から読み取りますã?"                            ).append( CR );
355                    buf.append( "(自動セãƒ?ƒˆではなã�??で、å¿?¦�に応じて設定してください)"                                            ).append( CR );
356                    buf.append( "useNumber の初期値は、\"true\" ですã?"                                                                                        ).append( CR );
357                    buf.append( CR );
358                    buf.append( "引数æ–?­—å?中に空白を含ã‚??合ã?、ダブルコーãƒ??ション(\"\") で括って下さã�??" ).append( CR );
359                    buf.append( "引数æ–?­—å?の ã€?』ã?前後には、空白は挟めません。å¿?�šã€?key=value の様に"             ).append( CR );
360                    buf.append( "繋げてくださいã€?                                                                                                                              ).append( CR );
361                    buf.append( CR ).append( CR );
362    
363                    buf.append( getArgument().usage() ).append( CR );
364    
365                    return buf.toString();
366            }
367    
368            /**
369             * こã?クラスは、main メソãƒ?ƒ‰から実行できませんã€?
370             *
371             * @param       args    コマンド引数配å?
372             */
373            public static void main( final String[] args ) {
374                    LogWriter.log( new Process_TableReader().usage() );
375            }
376    }