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