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 java.io.File;
019import java.io.BufferedReader;
020import java.io.PrintWriter;
021import java.io.IOException;
022import java.nio.charset.CharacterCodingException;                                       // 6.3.1.0 (2015/06/28)
023
024import org.opengion.fukurou.system.OgRuntimeException ;                         // 6.4.2.0 (2016/01/29)
025import org.opengion.fukurou.system.OgCharacterException ;                       // 6.5.0.1 (2016/10/21)
026import org.opengion.fukurou.system.DateSet;                                                     // 6.4.2.0 (2016/01/29)
027import org.opengion.fukurou.system.Closer;
028import org.opengion.fukurou.util.FileUtil;
029import org.opengion.fukurou.util.CommentLineParser;
030import org.opengion.fukurou.util.FileInfo;                                                      // 6.4.0.2 (2015/12/11)
031import org.opengion.fukurou.security.HybsCryptography ;                         // 5.7.2.1 (2014/01/17)
032import static org.opengion.fukurou.system.HybsConst.CR;                         // 6.3.1.0 (2015/06/28)
033
034/**
035 * FileLineModel は、LineModel を継承した ファイルリスト専用の
036 * LineModel の実装クラスです。
037 *
038 * FileLineModel オブジェクトには、ファイル属性(Level,File,Length,Modify,LineCnt,Biko,MD5)
039 * が設定されます。
040 * オプションで、FILEPATH,ADDRESS,FILENAME 属性を文字列で準備できます。(6.3.1.0 (2015/06/28))
041 * ADDRESS は、指定ファイルの親フォルダ。FILENAME はファイル名。FILEPATH は、ファイル名を含む
042 * 完全なファイルパスになります。
043 * ※ 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
044 *    この、新しい属性に、値をセットする場合は、useFilePath="true" をセットしてください。
045 * ※ 6.3.1.1 (2015/07/10) Modify のフォーマット(modifyForm)を、指定可能にします。
046 *    これは、Date型のまま、扱いたい所だが、文字列化しています。
047 *    初期値は、"yyyy/MM/dd HH:mm:ss" です。
048 *
049 * LineCnt と、MD5 は、それぞれ、計算するかどうかのフラグを設定する必要があります。
050 *
051 * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。
052 *    一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、
053 *    先のLength(文字数)値とは異なりますのでご注意ください。
054 *
055 * omitCmnt=true にすると、コメント部分を削除した行数と文字数を求めます。
056 * これは、/* から */ の間、// から改行までです。
057 * ただし、"(二重引用符)で囲まれた文字列は、コメントとみなしません。
058 *
059 * 8.1.0.4 (2022/01/28)
060 *  さらに、各行の空行はカウントに含めないことにします。
061 *
062 * データの1行分を FileLineModel に割り当てます。
063 * カラム番号は、0 から始まります。カラム名よりカラム番号を求める場合に、
064 * 存在しない場合は、-1 を返します。
065 * カラム番号が -1 の場合は、処理を行いません。
066 *
067 * 注意:このクラスは、同期処理されていません。
068 *
069 * @version  4.0
070 * @author   Kazuhiko Hasegawa
071 * @since    JDK5.0,
072 */
073public class FileLineModel extends LineModel {
074        /** 5.7.2.1 (2014/01/17) MD5 キー項目 */
075        // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
076        private static final String[] KEYS = { "Level","File","Length","Modify","LineCnt","Biko","MD5","FILEPATH","ADDRESS","FILENAME" };
077
078        private static final int LEVEL          = 0;
079        private static final int FILE           = 1;
080        private static final int LENGTH         = 2;
081        private static final int MODIFY         = 3;
082        private static final int LINECNT        = 4;
083        private static final int BIKO           = 5;
084        private static final int MD5            = 6;            // 5.7.2.1 (2014/01/17)
085        private static final int FILEPATH       = 7;            // 6.3.1.0 (2015/06/28)
086        private static final int ADDRESS        = 8;            // 6.3.1.0 (2015/06/28)
087        private static final int FILENAME       = 9;            // 6.3.1.0 (2015/06/28)
088
089        private final boolean useLineCnt ;
090        /** 5.7.2.1 (2014/01/17) MD5 項目追加 */
091        private final boolean useMD5 ;
092        /** 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する) */
093        private final boolean omitCmnt ;
094        /** 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性を使う場合は、true */
095        private final boolean useFilePath ;
096        /** 5.7.4.0 (2014/03/07) コメント削除時の文字数計算で利用するファイルのエンコード */
097        private String encode = "JISAutoDetect";
098        private String modifyForm = "yyyy/MM/dd HH:mm:ss" ;     // 6.3.1.1 (2015/07/10)
099
100        /**
101         * コンストラクターです。
102         * useLineCnt=false , useMD5=false , 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         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
107         *
108         */
109        public FileLineModel() {
110                this( false,false,false,false );                // 6.3.1.0 (2015/06/28)
111        }
112
113        /**
114         * ラインカウントの有無を指定した、コンストラクターです。
115         * useMD5=false , omitCmnt=false で初期化されます。
116         *
117         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
118         * @og.rev 5.7.2.1 (2014/01/17) MD5対応
119         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
120         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
121         *
122         * @param       isLineCnt       行数カウントの使用有無
123         */
124        public FileLineModel( final boolean isLineCnt ) {
125                this( isLineCnt,false,false,false );            // 6.3.1.0 (2015/06/28)
126        }
127
128        /**
129         * ラインカウントの有無と、MD5計算の有無を指定した、コンストラクターです。
130         * omitCmnt=false で初期化されます。
131         *
132         * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
133         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
134         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加。
135         *
136         * @param       isLineCnt       行数カウントの使用有無
137         * @param       isMD5           ファイルのMD5の使用有無
138         */
139        public FileLineModel( final boolean isLineCnt,final boolean isMD5 ) {
140                this( isLineCnt,isMD5,false,false );            // 6.3.1.0 (2015/06/28)
141        }
142
143        /**
144         * ラインカウントの有無と、MD5計算の有無と、コメント除外の可否を指定した、コンストラクターです。
145         *
146         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
147         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
148         *
149         * @param       isLineCnt       行数カウントの使用有無
150         * @param       isMD5           ファイルのMD5の使用有無
151         * @param       isOmit          コメント除外の可否(true:除外する)
152         */
153        public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit ) {
154                this( isLineCnt,isMD5,isOmit,false );   // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
155        }
156
157        /**
158         * ラインカウントの有無と、MD5計算の有無と、コメント除外の可否と、追加属性可否を指定した、コンストラクターです。
159         *
160         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)
161         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
162         * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
163         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: [this-escape] サブクラスが初期化される前の'this'エスケープの可能性があります
164         *
165         * @param       isLineCnt       行数カウントの使用有無
166         * @param       isMD5           ファイルのMD5の使用有無
167         * @param       isOmit          コメント除外の可否(true:除外する)
168         * @param       isPath          FILEPATH,ADDRESS,FILENAME 属性の可否(true:使用する)
169         */
170        public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit,final boolean isPath ) {
171                super( KEYS );                                          // 8.5.3.2 (2023/10/13) JDK21対応
172//              super();
173                // 4.3.4.4 (2009/01/01)
174                useLineCnt      = isLineCnt;
175                useMD5          = isMD5;                                // 5.7.2.1 (2014/01/17)
176                omitCmnt        = isOmit;                               // 5.7.4.0 (2014/03/07)
177                useFilePath     = isPath;                               // 5.7.4.0 (2014/03/07)
178//              init( KEYS );
179        }
180
181        /**
182         * LineModel を元に、FileLineModel を構築します。
183         * これは、一旦ファイル等にセーブされた FileLineModel 形式を
184         * 元に戻す簡易コンストラクタです。
185         *
186         * @og.rev 4.2.3.0 (2008/05/26) 新規追加
187         * @og.rev 5.7.2.1 (2014/01/17) MD5の設定処理追加
188         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
189         * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
190         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: [this-escape] サブクラスが初期化される前の'this'エスケープの可能性があります
191         *
192         * @param       model   元のLineModel
193         */
194        public FileLineModel( final LineModel model ) {
195                super( model.getNames() );                      // 8.5.3.2 (2023/10/13) JDK21対応
196//              super();
197//              // 4.3.4.4 (2009/01/01)
198//              init( model.getNames() );
199
200                final Object[] obj = model.getValues();
201
202                // 8.5.3.2 (2023/10/13) JDK21対応。setValue が使えないので、コンストラクタから分離する。
203//              setValue( LEVEL   ,Integer.valueOf( (String)obj[LEVEL] ) );
204//              setValue( FILE    ,new File((String)obj[FILE]) );
205//              setValue( LENGTH  ,Long.valueOf( (String)obj[LENGTH] ) );
206//              setValue( MODIFY  ,(String)obj[MODIFY] );
207
208                final String cnt = (String)obj[LINECNT] ;
209                useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ;
210//              if( useLineCnt ) { setValue( LINECNT ,cnt ); }
211
212//              setValue( BIKO  ,(String)obj[BIKO] );
213
214                // 5.7.2.1 (2014/01/17)
215                final String md5Data = (String)obj[MD5] ;
216                useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ;
217//              if( useMD5 ) { setValue( MD5 ,md5Data ); }
218
219                omitCmnt = false;                       // 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので、強制設定します。
220
221                // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
222                // 念のため、配列数をチェックしながら処理します。
223                if( obj.length > FILEPATH ) {
224                        final String path = (String)obj[FILEPATH] ;
225                        useFilePath = path != null && path.length() > 0 && ! "null".equalsIgnoreCase( path ) ;
226//                      if( useFilePath ) {
227//                              setValue( FILEPATH ,path );
228//                              if( obj.length > ADDRESS  ) { setValue( ADDRESS  ,(String)obj[ADDRESS]  ); }
229//                              if( obj.length > FILENAME ) { setValue( FILENAME ,(String)obj[FILENAME] ); }
230//                      }
231                }
232                else {
233                        useFilePath = false;
234                }
235        }
236
237        /**
238         * LineModel を元に、FileLineModel を構築します。
239         * <del>これは、一旦ファイル等にセーブされた FileLineModel 形式を元に戻す簡易コンストラクタです。</del>
240         * ※ 元々、コンストラクタでしたが、JDK21で警告が出たため、メソッドを分離しました。処理的には非効率です。
241         *
242         * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: [this-escape] サブクラスが初期化される前の'this'エスケープの可能性があります
243         *
244         * @param       model   元のLineModel
245         */
246//      public FileLineModel( final LineModel model ) {
247        public final void copyLineModel( final LineModel model ) {
248//              super( model.getNames() );                      // 8.5.3.2 (2023/10/13) JDK21対応
249//              super();
250//              // 4.3.4.4 (2009/01/01)
251//              init( model.getNames() );
252
253                final Object[] obj = model.getValues();
254
255                setValue( LEVEL   ,Integer.valueOf( (String)obj[LEVEL] ) );
256                setValue( FILE    ,new File((String)obj[FILE]) );
257                setValue( LENGTH  ,Long.valueOf( (String)obj[LENGTH] ) );
258                setValue( MODIFY  ,(String)obj[MODIFY] );
259
260                final String cnt = (String)obj[LINECNT] ;
261//              useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ;
262                if( useLineCnt ) { setValue( LINECNT ,cnt ); }
263
264                setValue( BIKO  ,(String)obj[BIKO] );
265
266                // 5.7.2.1 (2014/01/17)
267                final String md5Data = (String)obj[MD5] ;
268//              useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ;
269                if( useMD5 ) { setValue( MD5 ,md5Data ); }
270
271//              omitCmnt   = false;                     // 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので、強制設定します。
272
273                // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
274                // 念のため、配列数をチェックしながら処理します。
275                if( obj.length > FILEPATH ) {
276                        final String path = (String)obj[FILEPATH] ;
277//                      useFilePath = path != null && path.length() > 0 && ! "null".equalsIgnoreCase( path ) ;
278                        if( useFilePath ) {
279                                setValue( FILEPATH ,path );
280                                if( obj.length > ADDRESS  ) { setValue( ADDRESS  ,(String)obj[ADDRESS]  ); }
281                                if( obj.length > FILENAME ) { setValue( FILENAME ,(String)obj[FILENAME] ); }
282                        }
283                }
284//              else {
285//                      useFilePath = false;
286//              }
287        }
288
289//      /**
290//       * LineModel を元に、FileLineModel を構築します。
291//       * これは、一旦ファイル等にセーブされた FileLineModel 形式を
292//       * 元に戻す簡易コンストラクタです。
293//       *
294//       * @og.rev 4.2.3.0 (2008/05/26) 新規追加
295//       * @og.rev 5.7.2.1 (2014/01/17) MD5の設定処理追加
296//       * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
297//       * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
298//       * @og.rev 8.5.3.2 (2023/10/13) JDK21対応。警告: [this-escape] サブクラスが初期化される前の'this'エスケープの可能性があります … 廃止
299//       *
300//       * @param       model   元のLineModel
301//       */
302//      public FileLineModel( final LineModel model ) {
303//              super( model.getNames() );                      // 8.5.3.2 (2023/10/13) JDK21対応
304////            super();
305////            // 4.3.4.4 (2009/01/01)
306////            init( model.getNames() );
307//
308//              final Object[] obj = model.getValues();
309//
310//              setValue( LEVEL   ,Integer.valueOf( (String)obj[LEVEL] ) );
311//              setValue( FILE    ,new File((String)obj[FILE]) );
312//              setValue( LENGTH  ,Long.valueOf( (String)obj[LENGTH] ) );
313//              setValue( MODIFY  ,(String)obj[MODIFY] );
314//
315//              final String cnt = (String)obj[LINECNT] ;
316//              useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ;
317//              if( useLineCnt ) { setValue( LINECNT ,cnt ); }
318//
319//              setValue( BIKO  ,(String)obj[BIKO] );
320//
321//              // 5.7.2.1 (2014/01/17)
322//              final String md5Data = (String)obj[MD5] ;
323//              useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ;
324//              if( useMD5 ) { setValue( MD5 ,md5Data ); }
325//
326//              omitCmnt   = false;                     // 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので、強制設定します。
327//
328//              // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
329//              // 念のため、配列数をチェックしながら処理します。
330//              if( obj.length > FILEPATH ) {
331//                      final String path = (String)obj[FILEPATH] ;
332//                      useFilePath = path != null && path.length() > 0 && ! "null".equalsIgnoreCase( path ) ;
333//                      if( useFilePath ) {
334//                              setValue( FILEPATH ,path );
335//                              if( obj.length > ADDRESS  ) { setValue( ADDRESS  ,(String)obj[ADDRESS]  ); }
336//                              if( obj.length > FILENAME ) { setValue( FILENAME ,(String)obj[FILENAME] ); }
337//                      }
338//              }
339//              else {
340//                      useFilePath = false;
341//              }
342//      }
343
344        /**
345         * File属性値をセットします。
346         * LEVEL,FILE,LENGTH,MODIFY,LINECNT,MD5 の各属性を設定します。
347         *
348         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
349         * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します。
350         * @og.rev 5.7.2.1 (2014/01/17) MD5計算処理の追加
351         * @og.rev 5.7.4.0 (2014/03/07) コメント除外の可否(true:除外する)対応
352         * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
353         * @og.rev 6.2.1.0 (2015/03/13) ファイルの削除に失敗するため、削除しない。
354         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
355         * @og.rev 8.1.2.0 (2022/03/10) getMD5 メソッドを getHash メソッドに変更
356         *
357         * @param       level   ファイルのディレクトリ階層
358         * @param       file    ファイルオブジェクト
359         */
360        public void setFileVals( final int level, final File file ) {
361                // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
362//              setValue( LEVEL  ,Integer.valueOf( level ) );
363                setValue( LEVEL  ,level );
364                setValue( FILE   ,file );
365                setValue( MODIFY ,DateSet.getDate( file.lastModified(),modifyForm ) );                                  // 6.3.1.1 (2015/07/10)
366
367                // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
368                // 別にコメント除去されたファイルを作成して、それの MD5 を求める。
369                File ocFile = null;
370                if( omitCmnt && useMD5 ) {
371                        try {
372                                ocFile = File.createTempFile( "temp",".tmp" );
373                                ocFile.deleteOnExit();                                                          // 一応、このメソッド内で削除しますが、念のため。
374                        }
375                        catch( final IOException ex ) {
376                                final String errMsg = "コメント除外のMD5計算用 temp ファイルの作成に失敗しました。" + ex.getMessage() ;
377                                throw new OgRuntimeException( errMsg,ex );
378                        }
379                }
380
381                if( useLineCnt || omitCmnt ) {
382                        final long[] cntVals = getLineCnt( file,ocFile );                       // 5.7.7.1 (2014/06/13) 出力ファイルを渡します。
383                        setValue( LINECNT ,String.valueOf( cntVals[0] ) );
384                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
385//                      setValue( LENGTH  ,Long.valueOf(   cntVals[1] ) );
386                        setValue( LENGTH  ,cntVals[1] );
387                }
388                else {
389                        // 2.0.0 (2024/01/12) PMD 7.0.0 UnnecessaryBoxing
390//                      setValue( LENGTH  ,Long.valueOf( file.length() ) );
391                        setValue( LENGTH  ,file.length() );
392                }
393
394                // 5.7.2.1 (2014/01/17) MD5計算がtrue で、かつ、ファイルの場合、MD5 計算を行います。
395                if( useMD5 && file.isFile() ) {
396                        // 5.7.7.1 (2014/06/13) omitCmnt を考慮したMD5計算
397                        if( ocFile == null ) {
398//                              setValue( MD5 ,HybsCryptography.getMD5( file ) );                               // 8.1.2.0 (2022/03/10) Modify
399                                setValue( MD5 ,HybsCryptography.getHash( "MD5", file ) );
400                        }
401                        else {
402//                              setValue( MD5 ,HybsCryptography.getMD5( ocFile ) );                             // 8.1.2.0 (2022/03/10) Modify
403                                setValue( MD5 ,HybsCryptography.getHash( "MD5", ocFile ) );
404                                // 6.0.2.4 (2014/10/17) RV  java.io.File.delete() の例外的戻り値を無視しています。
405                                // 6.2.1.0 (2015/03/13) ファイルの削除に失敗するため、削除しない。
406                        }
407                }
408
409                // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
410                if( useFilePath ) {
411                        // FILEPATH は、正規のパス名文字列 を求めるが、エラー時は、絶対パス名文字列 にする。
412                        try {
413                                setValue( FILEPATH      , file.getCanonicalPath() );    // 正規のパス名文字列
414                        }
415                        catch( final IOException ex ) {
416                                setValue( FILEPATH      , file.getAbsolutePath() );             // 絶対パス名文字列
417                        }
418
419                        // ADDRESS は、親の名前なので、直フォルダ名になる。
420                        final File parent = file.getParentFile();
421                        if( parent != null ) {
422                                setValue( ADDRESS       ,parent.getName() );
423                        }
424
425                        setValue( FILENAME      ,file.getName() );
426                }
427        }
428
429        /**
430         * コメント削除時の文字数計算で利用するファイルのエンコードをセットします。
431         * 初期値:JISAutoDetect
432         *
433         * @og.rev 5.7.4.0 (2014/03/07) 新規追加
434         *
435         * @param       encode  コメント削除時の文字数計算で利用するファイルのエンコード
436         */
437        public void setEncode( final String encode ) {
438                this.encode = encode;
439        }
440
441        /**
442         * File属性値をセットします。
443         *
444         * @param       file    ファイルオブジェクト
445         */
446        public void setFile( final File file ) {
447                setValue( FILE,file );
448        }
449
450        /**
451         * ファイルを取得します。
452         *
453         * @return      ファイル
454         */
455        public File getFile() {
456                return (File)getValue( FILE );
457        }
458
459        /**
460         * 備考情報属性値をセットします。
461         *
462         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
463         *
464         * @param       biko    備考情報
465         */
466        public void setBiko( final String biko ) {
467                setValue( BIKO,biko );
468        }
469
470        /**
471         * レベル File属性値を取得します。
472         *
473         * @return      ファイルのディレクトリ階層
474         */
475        public int getLevel() {
476//              return ((Integer)getValue( LEVEL )).intValue();
477                return (Integer)getValue( LEVEL );
478        }
479
480        /**
481         * ファイルサイズ File属性値を取得します。
482         *
483         * @return      ファイルサイズ
484         */
485        public long getLength() {
486//              return ((Long)getValue( LENGTH )).longValue();
487                return (Long)getValue( LENGTH );
488        }
489
490        /**
491         * 更新日時(Modify) のフォーマットを、指定します。
492         *
493         * ここで指定しない場合は、"yyyy/MM/dd HH:mm:ss" になります。
494         * Date型で変換できないようなフォームを指定した場合は、実行時に
495         * エラーになりますので、ご注意ください。
496         *
497         * @og.rev 6.3.1.1 (2015/07/10) Modify のフォーマットを、指定可能にします。
498         *
499         * @param       form 更新日時のフォーマット
500         * @see         java.text.SimpleDateFormat
501         */
502        public void setModifyForm( final String form ) {
503                if( form != null && !form.isEmpty() ) {
504                        modifyForm = form;
505                }
506        }
507
508        /**
509         * 更新日時 File属性値を取得します。
510         *
511         * @return      更新日時(yyyy/MM/dd HH:mm:ss)
512         */
513        public String getModify() {
514                return (String)getValue( MODIFY );
515        }
516
517        /**
518         * MD5 File属性値を取得します。
519         * ただし、useMD5 が true でないと値は返しません。
520         *
521         * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
522         *
523         * @return      MD5の値
524         */
525        public String getMD5() {
526                return (String)getValue( MD5 );
527        }
528
529        /**
530         * 行数と文字数を取得します。
531         * 行数カウントとファイルの文字数カウント(バイト数ではありません)を行います。
532         * ※ useLineCnt=false の場合のLength(文字数)は、File#length() メソッドで求めます。
533         *    一方、useLineCnt=true にすると、行単位に、String#length() を加算するため、
534         *    先のLength(文字数)値とは異なりますのでご注意ください。
535         *
536         * 結果は、long型の配列で返します。[0]が行数で、[1]が文字数です。
537         * omitCmnt 属性を使用した場合は、コメント部分を削除した行数と文字数を求めます。
538         * これは、/&#042; から &#042;/ の間、&#47;&#47; から改行までです。
539         * ただし、&#034;(二重引用符)で囲まれた文字列は、コメントとみなしません。
540         *
541         * @og.rev 5.7.4.0 (2014/03/07) 行数カウントとファイルの文字数カウントを行う。
542         * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
543         * @og.rev 6.2.1.0 (2015/03/13) ディレクトリ以外からファイルのみに対象を変更。
544         * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。
545         * @og.rev 6.4.0.2 (2015/12/11) CommentLineParser 改造。
546         * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は、OgCharacterException に変換する。
547         * @og.rev 8.1.0.4 (2022/01/28) 各行の空行はカウントに含めないことにする。
548         *
549         * @param       file    行数を数えるファイルオブジェクト
550         * @param       ocFile  omitCmnt=trueの場合に、MD5計算する時の、仮出力ファイル(nullの場合は、無視)
551         *
552         * @return  long型の配列([0]が行数で、[1]が文字数)
553         * @og.rtnNotNull
554         */
555        private long[] getLineCnt( final File file,final File ocFile ) {
556                long lineCnt = 0L;              // 行数
557                long charCnt = 0L;              // 文字数
558
559                final BufferedReader reader = FileUtil.getBufferedReader( file,encode );
560
561                // 5.7.7.1 (2014/06/13) omitCmnt=true(コメント除外する) and useMD5=true(MD5計算する) 場合の処理
562                PrintWriter writer = null;
563                if( ocFile != null ) { writer = FileUtil.getPrintWriter( ocFile ,encode ); }
564
565                // 6.4.0.2 (2015/12/11) CommentLineParser 改造
566                final CommentLineParser clp = omitCmnt ? new CommentLineParser( FileInfo.getSUFIX( file ) ) : null;
567                try {
568                        // 6.2.1.0 (2015/03/13) ディレクトリ以外からファイルのみに対象を変更。
569                        if( file.isFile() ) {
570                                String line ;
571                                while((line = reader.readLine()) != null) {
572                                        if( omitCmnt ) {
573                                                line = clp.line( line );
574                                                // 8.1.0.4 (2022/01/28) 各行の空行はカウントに含めないことにする。
575//                                              if( line == null ) { continue; }        // 戻り値が null の場合は、行として不成立
576                                                if( line == null || line.isEmpty() ) { continue; }      // 戻り値が nullか 空行の場合は、含めない
577                                                if( writer != null ) { writer.println( line ); }        // 5.7.7.1 (2014/06/13)
578                                        }
579
580                                        lineCnt++;
581                                        charCnt += line.length();
582                                }
583                        }
584                }
585                // 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで、エラーになる。
586                catch( final CharacterCodingException ex ) {
587                        final String errMsg = "文字のエンコード・エラーが発生しました。" + CR
588                                                                +       "  ファイルのエンコードが指定のエンコードと異なります。" + CR
589                                                                +       " [" + file.getPath() + "] , Encode=[" + encode + "]" ;
590                        throw new OgCharacterException( errMsg,ex );    // 6.5.0.1 (2016/10/21)
591                }
592                catch( final IOException ex ) {
593                        final String errMsg = "ファイルカウント中に例外が発生しました。" + CR
594                                                                +       " [" + file.getPath() + "] , Encode=[" + encode + "]" ;
595                        throw new OgRuntimeException( errMsg,ex );
596                }
597                finally {
598                        Closer.ioClose( reader ) ;
599                        Closer.ioClose( writer ) ;              // 5.7.7.1 (2014/06/13) ioClose は、引数が null なら無視します。
600                }
601
602                return new long[] { lineCnt,charCnt };
603        }
604}