    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 * これは、/&#042; から &#042;/ の間、&#47;&#47; から改行までです。
    044 * ただし、&#034;(二重引用符)で囲まれた文字列は、コメントとみなしません。
    045 *
    046 * データの１行分を 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         * これは、/&#042; から &#042;/ の間、&#47;&#47; から改行までです。
    311         * ただし、&#034;(二重引用符)で囲まれた文字列は、コメントとみなしません。
    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}