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 */
016package org.opengion.fukurou.process;
017
018import org.opengion.fukurou.util.FileUtil;
019import org.opengion.fukurou.util.Closer;
020import org.opengion.fukurou.util.HybsDateUtil;
021import org.opengion.fukurou.util.CommentLineParser;
022
023import org.opengion.fukurou.security.HybsCryptography ;         // 5.7.2.1 (2014/01/17)
024
025import java.io.File;
026import java.io.BufferedReader;
027import java.io.PrintWriter;
028import java.io.IOException;
029
030/**
031 * FileLineModel は、LineModel を継承した ファイルリスト専用の
032 * LineModel の実装クラスです。
033 *
034 * FileLineModel オブジェクトには、ファイル属性(Level,File,Length,Modify,LineCnt,Biko,MD5)
035 * が設定されます。
036 * LineCnt と、MD5 は、それぞれ、計算するかどうかのフラグを設定する必要があります。
037 *
038 * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。
039 *    一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、
040 *    先のLength(文字数)値とは異なりますのでご注意ください。
041 *
042 * omitCmnt=true にすると、コメント部分を削除した行数と文字数を求めます。
043 * これは、/* から */ の間、// から改行までです。
044 * ただし、"(二重引用符)で囲まれた文字列は、コメントとみなしません。
045 *
046 * データの1行分を FileLineModel に割り当てます。
047 * カラム番号は、0 から始まります。カラム名よりカラム番号を求める場合に、
048 * 存在しない場合は、-1 を返します。
049 * カラム番号が -1 の場合は、処理を行いません。
050 *
051 * 注意:このクラスは、同期処理されていません。
052 *
053 * @version  4.0
054 * @author   Kazuhiko Hasegawa
055 * @since    JDK5.0,
056 */
057public class FileLineModel extends LineModel {
058        // 5.7.2.1 (2014/01/17) MD5 項目追加
059        private static final String[] KEYS = new String[] { "Level","File","Length","Modify","LineCnt","Biko","MD5" };
060
061        private static final int LEVEL    = 0;
062        private static final int FILE     = 1;
063        private static final int LENGTH   = 2;
064        private static final int MODIFY   = 3;
065        private static final int LINECNT  = 4;
066        private static final int BIKO     = 5;
067        private static final int MD5      = 6;          // 5.7.2.1 (2014/01/17)
068
069        private final boolean useLineCnt ;
070        private final boolean useMD5 ;                          // 5.7.2.1 (2014/01/17) MD5 項目追加
071        private final boolean omitCmnt ;                        // 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
072        private String encode = "JISAutoDetect";        // 5.7.4.0 (2014/03/07) コメント削除時の文字数計算で利用するファイルのエンコード
073
074        /**
075         * コンストラクターです。
076         * useLineCnt=false , useMD5=false , omitCmnt=false で初期化されます。
077         *
078         * @og.rev 5.7.2.1 (2014/01/17) MD5対応
079         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
080         *
081         */
082        public FileLineModel() {
083                this( false,false,false );                              // 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
084        }
085
086        /**
087         * ラインカウントの有無を指定した、コンストラクターです。
088         * useMD5=false , omitCmnt=false で初期化されます。
089         *
090         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
091         * @og.rev 5.7.2.1 (2014/01/17) MD5対応
092         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
093         *
094         * @param       isLineCnt       行数カウントの使用有無
095         */
096        public FileLineModel( final boolean isLineCnt ) {
097                this( isLineCnt,false,false );                  // 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
098        }
099
100        /**
101         * ラインカウントの有無と、MD5計算の有無を指定した、コンストラクターです。
102         * omitCmnt=false で初期化されます。
103         *
104         * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
105         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
106         *
107         * @param       isLineCnt       行数カウントの使用有無
108         * @param       isMD5           ファイルのMD5の使用有無
109         */
110        public FileLineModel( final boolean isLineCnt,final boolean isMD5 ) {
111                this( isLineCnt,isMD5,false );                          // 5.7.4.0 (2014/03/07) コメント除外
112        }
113
114        /**
115         * ラインカウントの有無と、MD5計算の有無と、コメント除外の可否を指定した、コンストラクターです。
116         *
117         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
118         *
119         * @param       isLineCnt       行数カウントの使用有無
120         * @param       isMD5           ファイルのMD5の使用有無
121         * @param       isOmit          コメント除外の可否(true:除外する)
122         */
123        public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit ) {
124                // 4.3.4.4 (2009/01/01)
125                useLineCnt = isLineCnt;
126                useMD5     = isMD5;                             // 5.7.2.1 (2014/01/17)
127                omitCmnt   = isOmit;                    // 5.7.4.0 (2014/03/07)
128                init( KEYS );
129        }
130
131        /**
132         * LineModel を元に、FileLineModel を構築します。
133         * これは、一旦ファイル等にセーブされた FileLineModel 形式を
134         * 元に戻す簡易コンストラクタです。
135         *
136         * @og.rev 4.2.3.0 (2008/05/26) 新規追加
137         * @og.rev 5.7.2.1 (2014/01/17) MD5の設定処理追加
138         *
139         * @param       model   元のLineModel
140         */
141        public FileLineModel( final LineModel model ) {
142                // 4.3.4.4 (2009/01/01)
143                init( model.getNames() );
144
145                Object[] obj = model.getValues();
146
147                setValue( LEVEL   ,Integer.valueOf( (String)obj[LEVEL] ) );
148                setValue( FILE    ,new File((String)obj[FILE]) );
149                setValue( LENGTH  ,Long.valueOf( (String)obj[LENGTH] ) );
150                setValue( MODIFY  ,(String)obj[MODIFY] );
151
152                String cnt = (String)obj[LINECNT] ;
153                useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ;
154                if( useLineCnt ) { setValue( LINECNT ,cnt ); }
155
156                setValue( BIKO  ,(String)obj[BIKO] );
157
158                // 5.7.2.1 (2014/01/17) 
159                String md5Data = (String)obj[MD5] ;
160                useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ;
161                if( useMD5 ) { setValue( MD5 ,md5Data ); }
162
163                omitCmnt   = false;                     // 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので、強制設定します。
164        }
165
166        /**
167         * File属性値をセットします。
168         * LEVEL,FILE,LENGTH,MODIFY,LINECNT,MD5 の各属性を設定します。
169         *
170         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
171         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
172         * @og.rev 5.7.2.1 (2014/01/17) MD5計算処理の追加
173         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
174         * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
175         *
176         * @param       level   ファイルのディレクトリ階層
177         * @param       file    ファイルオブジェクト
178         */
179        public void setFileVals( final int level, final File file ) {
180                setValue( LEVEL  ,Integer.valueOf( level ) );
181                setValue( FILE   ,file );
182                setValue( MODIFY ,HybsDateUtil.getDate( file.lastModified(),"yyyy/MM/dd HH:mm:ss" ) );          // 5.5.7.2 (2012/10/09) HybsDateUtil を利用する
183
184                // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
185                // 別にコメント除去されたファイルを作成して、それの MD5 を求める。
186                File ocFile = null;
187                if( omitCmnt && useMD5 ) {
188                        try {
189                                ocFile = File.createTempFile( "temp",".tmp" );
190                                ocFile.deleteOnExit();                                                          // 一応、このメソッド内で削除しますが、念のため。
191                        }
192                        catch( IOException ex ) {
193                                String errMsg = "コメント除外のMD5計算用 temp ファイルの作成に失敗しました。" + ex.getMessage() ;
194                                throw new RuntimeException( errMsg,ex );
195                        }
196                }
197
198                if( useLineCnt || omitCmnt ) {
199//                      long[] cntVals = getLineCnt( file );
200                        long[] cntVals = getLineCnt( file,ocFile );                     // 5.7.7.1 (2014/06/13) 出力ファイルを渡します。
201                        setValue( LINECNT ,String.valueOf( cntVals[0] ) );
202                        setValue( LENGTH  ,Long.valueOf(   cntVals[1] ) );
203                }
204                else {
205                        setValue( LENGTH  ,Long.valueOf( file.length() ) );
206                }
207
208                // 5.7.2.1 (2014/01/17) MD5計算がtrue で、かつ、ファイルの場合、MD5 計算を行います。
209                if( useMD5 && file.isFile() ) {
210                        // 5.7.7.1 (2014/06/13) omitCmnt を考慮したMD5計算
211                        if( ocFile == null ) {
212                                setValue( MD5 ,HybsCryptography.getMD5( file ) );
213                        }
214                        else {
215                                setValue( MD5 ,HybsCryptography.getMD5( ocFile ) );
216                                ocFile.delete();
217                        }
218                }
219        }
220
221        /**
222         * コメント削除時の文字数計算で利用するファイルのエンコードをセットします。
223         * 初期値:JISAutoDetect
224         *
225         * @og.rev 5.7.4.0 (2014/03/07) 新規追加
226         *
227         * @param       encode  コメント削除時の文字数計算で利用するファイルのエンコード
228         */
229        public void setEncode( final String encode ) {
230                this.encode = encode;
231        }
232
233        /**
234         * File属性値をセットします。
235         *
236         * @param       file    ファイルオブジェクト
237         */
238        public void setFile( final File file ) {
239                setValue( FILE,file );
240        }
241
242        /**
243         * 備考情報属性値をセットします。
244         *
245         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
246         *
247         * @param       biko    備考情報
248         */
249        public void setBiko( final String biko ) {
250                setValue( BIKO,biko );
251        }
252
253        /**
254         * レベル File属性値を取得します。
255         *
256         * @return      ファイルのディレクトリ階層
257         */
258        public int getLebel() {
259                return ((Integer)getValue( LEVEL )).intValue();
260        }
261
262        /**
263         * ファイルを取得します。
264         *
265         * @return      ファイル
266         */
267        public File getFile() {
268                return (File)getValue( FILE );
269        }
270
271        /**
272         * ファイルサイズ File属性値を取得します。
273         *
274         * @return      ファイルサイズ
275         */
276        public long getLength() {
277                return ((Long)getValue( LENGTH )).longValue();
278        }
279
280        /**
281         * 更新日時 File属性値を取得します。
282         *
283         * @return      更新日時(yyyy/MM/dd HH:mm:ss)
284         */
285        public String getModify() {
286                return (String)getValue( MODIFY );
287        }
288
289        /**
290         * MD5 File属性値を取得します。
291         * ただし、useMD5 が true でないと値は返しません。
292         *
293         * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
294         *
295         * @return      MD5の値
296         */
297        public String getMD5() {
298                return (String)getValue( MD5 );
299        }
300
301        /**
302         * 行数と文字数を取得します。
303         * 行数カウントとファイルの文字数カウント(バイト数ではありません)を行います。
304         * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。
305         *    一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、
306         *    先のLength(文字数)値とは異なりますのでご注意ください。
307         *
308         * 結果は、long型の配列で返します。[0]が行数で、[1]が文字数です。
309         * omitCmnt 属性を使用した場合は、コメント部分を削除した行数と文字数を求めます。
310         * これは、/* から */ の間、// から改行までです。
311         * ただし、"(二重引用符)で囲まれた文字列は、コメントとみなしません。
312         *
313         * @og.rev 5.7.4.0 (2014/03/07) 行数カウントとファイルの文字数カウントを行う。
314         * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
315         *
316         * @param       file    行数を数えるファイルオブジェクト
317         * @param       ocFile  omitCmnt=trueの場合に、MD5計算する時の、仮出力ファイル(nullの場合は、無視)
318         *
319         * @return  long型の配列([0]が行数で、[1]が文字数)
320         */
321//      private long[] getLineCnt( final File file ) {
322        private long[] getLineCnt( final File file,final File ocFile ) {
323                long lineCnt = 0L;              // 行数
324                long charCnt = 0L;              // 文字数
325
326                BufferedReader reader = FileUtil.getBufferedReader( file,encode );
327
328                // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
329                PrintWriter writer = null;
330                if( ocFile != null ) { writer = FileUtil.getPrintWriter( ocFile ,encode ); }
331
332                CommentLineParser clp = omitCmnt ? new CommentLineParser() : null;
333                try {
334                        if( ! file.isDirectory() ) {
335                                String line ;
336                                while((line = reader.readLine()) != null) {
337                                        if( omitCmnt ) {
338                                                line = clp.line( line );
339                                                if( line == null ) { continue; }        // 戻り値が null の場合は、行として不成立
340                                                if( writer != null ) { writer.println( line ); }        // 5.7.7.1 (2014/06/13)
341                                        }
342
343                                        lineCnt++;
344                                        charCnt += line.length();
345                                }
346                        }
347                }
348                catch( IOException ex ) {
349                        String errMsg = "ファイルカウント中に例外が発生しました。[" + file + "]" ;
350                        throw new RuntimeException( errMsg,ex );
351                }
352                finally {
353                        Closer.ioClose( reader ) ;
354                        Closer.ioClose( writer ) ;              // 5.7.7.1 (2014/06/13) ioClose は、引数が null なら無視します。
355                }
356
357                return new long[] { lineCnt,charCnt };
358        }
359}