/*
 * Copyright (c) 2009 The openGion Project.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific language
 * governing permissions and limitations under the License.
 */
package org.opengion.fukurou.process;

import org.opengion.fukurou.system.OgRuntimeException ;				// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.OgCharacterException ;			// 6.5.0.1 (2016/10/21)
import org.opengion.fukurou.system.DateSet;							// 6.4.2.0 (2016/01/29)
import org.opengion.fukurou.system.Closer;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.CommentLineParser;
import org.opengion.fukurou.util.FileInfo;							// 6.4.0.2 (2015/12/11)
import org.opengion.fukurou.security.HybsCryptography ;				// 5.7.2.1 (2014/01/17)
import static org.opengion.fukurou.system.HybsConst.CR;				// 6.3.1.0 (2015/06/28)

import java.io.File;
import java.io.BufferedReader;
import java.io.PrintWriter;
import java.io.IOException;
import java.nio.charset.CharacterCodingException;					// 6.3.1.0 (2015/06/28)

/**
 * FileLineModel は､LineModel を継承した ﾌｧｲﾙﾘｽﾄ専用の
 * LineModel の実装ｸﾗｽです｡
 *
 * FileLineModel ｵﾌﾞｼﾞｪｸﾄには､ﾌｧｲﾙ属性(Level,File,Length,Modify,LineCnt,Biko,MD5)
 * が設定されます｡
 * ｵﾌﾟｼｮﾝで､FILEPATH,ADDRESS,FILENAME 属性を文字列で準備できます｡(6.3.1.0 (2015/06/28))
 * ADDRESS は､指定ﾌｧｲﾙの親ﾌｫﾙﾀﾞ｡FILENAME はﾌｧｲﾙ名｡FILEPATH は､ﾌｧｲﾙ名を含む
 * 完全なﾌｧｲﾙﾊﾟｽになります｡
 * ※ 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
 *    この､新しい属性に､値をｾｯﾄする場合は､useFilePath="true" をｾｯﾄしてください｡
 * ※ 6.3.1.1 (2015/07/10) Modify のﾌｫｰﾏｯﾄ(modifyForm)を､指定可能にします｡
 *    これは､Date型のまま､扱いたい所だが､文字列化しています｡
 *    初期値は､"yyyy/MM/dd HH:mm:ss" です｡
 *
 * LineCnt と､MD5 は､それぞれ､計算するかどうかのﾌﾗｸﾞを設定する必要があります｡
 *
 * ※ useLineCnt=false の場合のLength(文字数)は､File#length() ﾒｿｯﾄﾞで求めます｡
 *    一方､useLineCnt=true にすると､行単位に､String#length() を加算するため､
 *    先のLength(文字数)値とは異なりますのでご注意ください｡
 *
 * omitCmnt=true にすると､ｺﾒﾝﾄ部分を削除した行数と文字数を求めます｡
 * これは､/&#042; から &#042;/ の間､&#47;&#47; から改行までです｡
 * ただし､&#034;(二重引用符)で囲まれた文字列は､ｺﾒﾝﾄとみなしません｡
 *
 * 8.1.0.4 (2022/01/28)
 *  さらに､各行の空行はｶｳﾝﾄに含めないことにします｡
 *
 * ﾃﾞｰﾀの１行分を FileLineModel に割り当てます｡
 * ｶﾗﾑ番号は､0 から始まります｡ｶﾗﾑ名よりｶﾗﾑ番号を求める場合に､
 * 存在しない場合は､-1 を返します｡
 * ｶﾗﾑ番号が -1 の場合は､処理を行いません｡
 *
 * 注意：このｸﾗｽは､同期処理されていません｡
 *
 * @version  4.0
 * @author   Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class FileLineModel extends LineModel {
	// 5.7.2.1 (2014/01/17) MD5 項目追加
	// 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	private static final String[] KEYS = { "Level","File","Length","Modify","LineCnt","Biko","MD5","FILEPATH","ADDRESS","FILENAME" };

	private static final int LEVEL		= 0;
	private static final int FILE		= 1;
	private static final int LENGTH		= 2;
	private static final int MODIFY		= 3;
	private static final int LINECNT	= 4;
	private static final int BIKO		= 5;
	private static final int MD5		= 6;		// 5.7.2.1 (2014/01/17)
	private static final int FILEPATH	= 7;		// 6.3.1.0 (2015/06/28)
	private static final int ADDRESS	= 8;		// 6.3.1.0 (2015/06/28)
	private static final int FILENAME	= 9;		// 6.3.1.0 (2015/06/28)

	private final boolean useLineCnt ;
	private final boolean useMD5 ;				// 5.7.2.1 (2014/01/17) MD5 項目追加
	private final boolean omitCmnt ;			// 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)
	private final boolean useFilePath ;			// 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性を使う場合は､true
	private String encode = "JISAutoDetect";	// 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ削除時の文字数計算で利用するﾌｧｲﾙのｴﾝｺｰﾄﾞ
	private String modifyForm = "yyyy/MM/dd HH:mm:ss" ;	// 6.3.1.1 (2015/07/10)

	/**
	 * ｺﾝｽﾄﾗｸﾀｰです｡
	 * useLineCnt=false , useMD5=false , omitCmnt=false で初期化されます｡
	 *
	 * @og.rev 5.7.2.1 (2014/01/17) MD5対応
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)対応
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 *
	 */
	public FileLineModel() {
		this( false,false,false,false );		// 6.3.1.0 (2015/06/28)
	}

	/**
	 * ﾗｲﾝｶｳﾝﾄの有無を指定した､ｺﾝｽﾄﾗｸﾀｰです｡
	 * useMD5=false , omitCmnt=false で初期化されます｡
	 *
	 * @og.rev 4.2.2.0 (2008/05/10) 行数ｶｳﾝﾄの使用有無
	 * @og.rev 5.7.2.1 (2014/01/17) MD5対応
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)対応
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 *
	 * @param	isLineCnt	行数ｶｳﾝﾄの使用有無
	 */
	public FileLineModel( final boolean isLineCnt ) {
		this( isLineCnt,false,false,false );		// 6.3.1.0 (2015/06/28)
	}

	/**
	 * ﾗｲﾝｶｳﾝﾄの有無と､MD5計算の有無を指定した､ｺﾝｽﾄﾗｸﾀｰです｡
	 * omitCmnt=false で初期化されます｡
	 *
	 * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)対応
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加｡
	 *
	 * @param	isLineCnt	行数ｶｳﾝﾄの使用有無
	 * @param	isMD5		ﾌｧｲﾙのMD5の使用有無
	 */
	public FileLineModel( final boolean isLineCnt,final boolean isMD5 ) {
		this( isLineCnt,isMD5,false,false );		// 6.3.1.0 (2015/06/28)
	}

	/**
	 * ﾗｲﾝｶｳﾝﾄの有無と､MD5計算の有無と､ｺﾒﾝﾄ除外の可否を指定した､ｺﾝｽﾄﾗｸﾀｰです｡
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 *
	 * @param	isLineCnt	行数ｶｳﾝﾄの使用有無
	 * @param	isMD5		ﾌｧｲﾙのMD5の使用有無
	 * @param	isOmit		ｺﾒﾝﾄ除外の可否(true:除外する)
	 */
	public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit ) {
		this( isLineCnt,isMD5,isOmit,false );	// 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	}

	/**
	 * ﾗｲﾝｶｳﾝﾄの有無と､MD5計算の有無と､ｺﾒﾝﾄ除外の可否と､追加属性可否を指定した､ｺﾝｽﾄﾗｸﾀｰです｡
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
	 *
	 * @param	isLineCnt	行数ｶｳﾝﾄの使用有無
	 * @param	isMD5		ﾌｧｲﾙのMD5の使用有無
	 * @param	isOmit		ｺﾒﾝﾄ除外の可否(true:除外する)
	 * @param	isPath		FILEPATH,ADDRESS,FILENAME 属性の可否(true:使用する)
	 */
	public FileLineModel( final boolean isLineCnt,final boolean isMD5,final boolean isOmit,final boolean isPath ) {
		super();
		// 4.3.4.4 (2009/01/01)
		useLineCnt	= isLineCnt;
		useMD5		= isMD5;				// 5.7.2.1 (2014/01/17)
		omitCmnt	= isOmit;				// 5.7.4.0 (2014/03/07)
		useFilePath	= isPath;				// 5.7.4.0 (2014/03/07)
		init( KEYS );
	}

	/**
	 * LineModel を元に､FileLineModel を構築します｡
	 * これは､一旦ﾌｧｲﾙ等にｾｰﾌﾞされた FileLineModel 形式を
	 * 元に戻す簡易ｺﾝｽﾄﾗｸﾀです｡
	 *
	 * @og.rev 4.2.3.0 (2008/05/26) 新規追加
	 * @og.rev 5.7.2.1 (2014/01/17) MD5の設定処理追加
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 * @og.rev 6.4.1.1 (2016/01/16) PMD refactoring. It is a good practice to call super() in a constructor
	 *
	 * @param	model	元のLineModel
	 */
	public FileLineModel( final LineModel model ) {
		super();
		// 4.3.4.4 (2009/01/01)
		init( model.getNames() );

		final Object[] obj = model.getValues();

		setValue( LEVEL   ,Integer.valueOf( (String)obj[LEVEL] ) );
		setValue( FILE    ,new File((String)obj[FILE]) );
		setValue( LENGTH  ,Long.valueOf( (String)obj[LENGTH] ) );
		setValue( MODIFY  ,(String)obj[MODIFY] );

		final String cnt = (String)obj[LINECNT] ;
		useLineCnt = cnt != null && cnt.length() > 0 && ! "null".equalsIgnoreCase( cnt ) ;
		if( useLineCnt ) { setValue( LINECNT ,cnt ); }

		setValue( BIKO  ,(String)obj[BIKO] );

		// 5.7.2.1 (2014/01/17)
		final String md5Data = (String)obj[MD5] ;
		useMD5 = md5Data != null && md5Data.length() > 0 && ! "null".equalsIgnoreCase( md5Data ) ;
		if( useMD5 ) { setValue( MD5 ,md5Data ); }

		omitCmnt   = false;			// 5.7.4.0 (2014/03/07) 既存の LineModel から取得できないので､強制設定します｡

		// 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
		// 念のため､配列数をﾁｪｯｸしながら処理します｡
		if( obj.length > FILEPATH ) {
			final String path = (String)obj[FILEPATH] ;
			useFilePath = path != null && path.length() > 0 && ! "null".equalsIgnoreCase( path ) ;
			if( useFilePath ) {
				setValue( FILEPATH ,path );
				if( obj.length > ADDRESS  ) { setValue( ADDRESS  ,(String)obj[ADDRESS]  ); }
				if( obj.length > FILENAME ) { setValue( FILENAME ,(String)obj[FILENAME] ); }
			}
		}
		else {
			useFilePath = false;
		}
	}

	/**
	 * File属性値をｾｯﾄします｡
	 * LEVEL,FILE,LENGTH,MODIFY,LINECNT,MD5 の各属性を設定します｡
	 *
	 * @og.rev 4.2.2.0 (2008/05/10) 行数ｶｳﾝﾄの使用有無
	 * @og.rev 5.5.7.2 (2012/10/09) HybsDateUtil を利用するように修正します｡
	 * @og.rev 5.7.2.1 (2014/01/17) MD5計算処理の追加
	 * @og.rev 5.7.4.0 (2014/03/07) ｺﾒﾝﾄ除外の可否(true:除外する)対応
	 * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(ｺﾒﾝﾄ除外する) and useMD5=true(MD5計算する) 場合の処理
	 * @og.rev 6.2.1.0 (2015/03/13) ﾌｧｲﾙの削除に失敗するため､削除しない｡
	 * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
	 * @og.rev 8.1.2.0 (2022/03/10) getMD5 ﾒｿｯﾄﾞを getHash ﾒｿｯﾄﾞに変更
	 *
	 * @param	level	ﾌｧｲﾙのﾃﾞｨﾚｸﾄﾘ階層
	 * @param	file	ﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
	 */
	public void setFileVals( final int level, final File file ) {
		setValue( LEVEL  ,Integer.valueOf( level ) );
		setValue( FILE   ,file );
		setValue( MODIFY ,DateSet.getDate( file.lastModified(),modifyForm ) );					// 6.3.1.1 (2015/07/10)

		// 5.7.7.1 (2014/06/13) omitCmnt=true(ｺﾒﾝﾄ除外する) and useMD5=true(MD5計算する) 場合の処理
		// 別にｺﾒﾝﾄ除去されたﾌｧｲﾙを作成して､それの MD5 を求める｡
		File ocFile = null;
		if( omitCmnt && useMD5 ) {
			try {
				ocFile = File.createTempFile( "temp",".tmp" );
				ocFile.deleteOnExit();								// 一応､このﾒｿｯﾄﾞ内で削除しますが､念のため｡
			}
			catch( final IOException ex ) {
				final String errMsg = "ｺﾒﾝﾄ除外のMD5計算用 temp ﾌｧｲﾙの作成に失敗しました｡" + ex.getMessage() ;
				throw new OgRuntimeException( errMsg,ex );
			}
		}

		if( useLineCnt || omitCmnt ) {
			final long[] cntVals = getLineCnt( file,ocFile );			// 5.7.7.1 (2014/06/13) 出力ﾌｧｲﾙを渡します｡
			setValue( LINECNT ,String.valueOf( cntVals[0] ) );
			setValue( LENGTH  ,Long.valueOf(   cntVals[1] ) );
		}
		else {
			setValue( LENGTH  ,Long.valueOf( file.length() ) );
		}

		// 5.7.2.1 (2014/01/17) MD5計算がtrue で､かつ､ﾌｧｲﾙの場合､MD5 計算を行います｡
		if( useMD5 && file.isFile() ) {
			// 5.7.7.1 (2014/06/13) omitCmnt を考慮したMD5計算
			if( ocFile == null ) {
//				setValue( MD5 ,HybsCryptography.getMD5( file ) );				// 8.1.2.0 (2022/03/10) Modify
				setValue( MD5 ,HybsCryptography.getHash( "MD5", file ) );
			}
			else {
//				setValue( MD5 ,HybsCryptography.getMD5( ocFile ) );				// 8.1.2.0 (2022/03/10) Modify
				setValue( MD5 ,HybsCryptography.getHash( "MD5", ocFile ) );
				// 6.0.2.4 (2014/10/17) RV  java.io.File.delete() の例外的戻り値を無視しています｡
				// 6.2.1.0 (2015/03/13) ﾌｧｲﾙの削除に失敗するため､削除しない｡
			}
		}

		// 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
		if( useFilePath ) {
			// FILEPATH は､正規のﾊﾟｽ名文字列 を求めるが､ｴﾗｰ時は､絶対ﾊﾟｽ名文字列 にする｡
			try {
				setValue( FILEPATH	, file.getCanonicalPath() );	// 正規のﾊﾟｽ名文字列
			}
			catch( final IOException ex ) {
				setValue( FILEPATH	, file.getAbsolutePath() );		// 絶対ﾊﾟｽ名文字列
			}

			// ADDRESS は､親の名前なので､直ﾌｫﾙﾀﾞ名になる｡
			final File parent = file.getParentFile();
			if( parent != null ) {
				setValue( ADDRESS	,parent.getName() );
			}

			setValue( FILENAME	,file.getName() );
		}
	}

	/**
	 * ｺﾒﾝﾄ削除時の文字数計算で利用するﾌｧｲﾙのｴﾝｺｰﾄﾞをｾｯﾄします｡
	 * 初期値:JISAutoDetect
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) 新規追加
	 *
	 * @param	encode	ｺﾒﾝﾄ削除時の文字数計算で利用するﾌｧｲﾙのｴﾝｺｰﾄﾞ
	 */
	public void setEncode( final String encode ) {
		this.encode = encode;
	}

	/**
	 * File属性値をｾｯﾄします｡
	 *
	 * @param	file	ﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
	 */
	public void setFile( final File file ) {
		setValue( FILE,file );
	}

	/**
	 * ﾌｧｲﾙを取得します｡
	 *
	 * @return	ﾌｧｲﾙ
	 */
	public File getFile() {
		return (File)getValue( FILE );
	}

	/**
	 * 備考情報属性値をｾｯﾄします｡
	 *
	 * @og.rev 4.2.2.0 (2008/05/10) 行数ｶｳﾝﾄの使用有無
	 *
	 * @param	biko	備考情報
	 */
	public void setBiko( final String biko ) {
		setValue( BIKO,biko );
	}

	/**
	 * ﾚﾍﾞﾙ File属性値を取得します｡
	 *
	 * @return	ﾌｧｲﾙのﾃﾞｨﾚｸﾄﾘ階層
	 */
	public int getLevel() {
		return ((Integer)getValue( LEVEL )).intValue();
	}

	/**
	 * ﾌｧｲﾙｻｲｽﾞ File属性値を取得します｡
	 *
	 * @return	ﾌｧｲﾙｻｲｽﾞ
	 */
	public long getLength() {
		return ((Long)getValue( LENGTH )).longValue();
	}

	/**
	 * 更新日時(Modify) のﾌｫｰﾏｯﾄを､指定します｡
	 *
	 * ここで指定しない場合は､"yyyy/MM/dd HH:mm:ss" になります｡
	 * Date型で変換できないようなﾌｫｰﾑを指定した場合は､実行時に
	 * ｴﾗｰになりますので､ご注意ください｡
	 *
	 * @og.rev 6.3.1.1 (2015/07/10) Modify のﾌｫｰﾏｯﾄを､指定可能にします｡
	 *
	 * @param	form 更新日時のﾌｫｰﾏｯﾄ
	 * @see		java.text.SimpleDateFormat
	 */
	public void setModifyForm( final String form ) {
		if( form != null && !form.isEmpty() ) {
			modifyForm = form;
		}
	}

	/**
	 * 更新日時 File属性値を取得します｡
	 *
	 * @return	更新日時(yyyy/MM/dd HH:mm:ss)
	 */
	public String getModify() {
		return (String)getValue( MODIFY );
	}

	/**
	 * MD5 File属性値を取得します｡
	 * ただし､useMD5 が true でないと値は返しません｡
	 *
	 * @og.rev 5.7.2.1 (2014/01/17) 新規追加(MD5対応)
	 *
	 * @return	MD5の値
	 */
	public String getMD5() {
		return (String)getValue( MD5 );
	}

	/**
	 * 行数と文字数を取得します｡
	 * 行数ｶｳﾝﾄとﾌｧｲﾙの文字数ｶｳﾝﾄ(ﾊﾞｲﾄ数ではありません)を行います｡
	 * ※ useLineCnt=false の場合のLength(文字数)は､File#length() ﾒｿｯﾄﾞで求めます｡
	 *    一方､useLineCnt=true にすると､行単位に､String#length() を加算するため､
	 *    先のLength(文字数)値とは異なりますのでご注意ください｡
	 *
	 * 結果は､long型の配列で返します｡[0]が行数で､[1]が文字数です｡
	 * omitCmnt 属性を使用した場合は､ｺﾒﾝﾄ部分を削除した行数と文字数を求めます｡
	 * これは､/&#042; から &#042;/ の間､&#47;&#47; から改行までです｡
	 * ただし､&#034;(二重引用符)で囲まれた文字列は､ｺﾒﾝﾄとみなしません｡
	 *
	 * @og.rev 5.7.4.0 (2014/03/07) 行数ｶｳﾝﾄとﾌｧｲﾙの文字数ｶｳﾝﾄを行う｡
	 * @og.rev 5.7.7.1 (2014/06/13) omitCmnt=true(ｺﾒﾝﾄ除外する) and useMD5=true(MD5計算する) 場合の処理
	 * @og.rev 6.2.1.0 (2015/03/13) ﾃﾞｨﾚｸﾄﾘ以外からﾌｧｲﾙのみに対象を変更｡
	 * @og.rev 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで､ｴﾗｰになる｡
	 * @og.rev 6.4.0.2 (2015/12/11) CommentLineParser 改造｡
	 * @og.rev 6.5.0.1 (2016/10/21) CharacterCodingException は､OgCharacterException に変換する｡
	 * @og.rev 8.1.0.4 (2022/01/28) 各行の空行はｶｳﾝﾄに含めないことにする｡
	 *
	 * @param	file	行数を数えるﾌｧｲﾙｵﾌﾞｼﾞｪｸﾄ
	 * @param	ocFile	omitCmnt=trueの場合に､MD5計算する時の､仮出力ﾌｧｲﾙ(nullの場合は､無視)
	 *
	 * @return  long型の配列([0]が行数で､[1]が文字数)
	 * @og.rtnNotNull
	 */
	private long[] getLineCnt( final File file,final File ocFile ) {
		long lineCnt = 0L;		// 行数
		long charCnt = 0L;		// 文字数

		final BufferedReader reader = FileUtil.getBufferedReader( file,encode );

		// 5.7.7.1 (2014/06/13) omitCmnt=true(ｺﾒﾝﾄ除外する) and useMD5=true(MD5計算する) 場合の処理
		PrintWriter writer = null;
		if( ocFile != null ) { writer = FileUtil.getPrintWriter( ocFile ,encode ); }

		// 6.4.0.2 (2015/12/11) CommentLineParser 改造
		final CommentLineParser clp = omitCmnt ? new CommentLineParser( FileInfo.getSUFIX( file ) ) : null;
		try {
			// 6.2.1.0 (2015/03/13) ﾃﾞｨﾚｸﾄﾘ以外からﾌｧｲﾙのみに対象を変更｡
			if( file.isFile() ) {
				String line ;
				while((line = reader.readLine()) != null) {
					if( omitCmnt ) {
						line = clp.line( line );
						// 8.1.0.4 (2022/01/28) 各行の空行はｶｳﾝﾄに含めないことにする｡
//						if( line == null ) { continue; }	// 戻り値が null の場合は､行として不成立
						if( line == null || line.isEmpty() ) { continue; }	// 戻り値が nullか 空行の場合は､含めない
						if( writer != null ) { writer.println( line ); }	// 5.7.7.1 (2014/06/13)
					}

					lineCnt++;
					charCnt += line.length();
				}
			}
		}
		// 6.3.1.0 (2015/06/28) nioを使用すると UTF-8とShuft-JISで､ｴﾗｰになる｡
		catch( final CharacterCodingException ex ) {
			final String errMsg = "文字のｴﾝｺｰﾄﾞ･ｴﾗｰが発生しました｡" + CR
								+	"  ﾌｧｲﾙのｴﾝｺｰﾄﾞが指定のｴﾝｺｰﾄﾞと異なります｡" + CR
								+	" [" + file.getPath() + "] , Encode=[" + encode + "]" ;
			throw new OgCharacterException( errMsg,ex );	// 6.5.0.1 (2016/10/21)
		}
		catch( final IOException ex ) {
			final String errMsg = "ﾌｧｲﾙｶｳﾝﾄ中に例外が発生しました｡" + CR
								+	" [" + file.getPath() + "] , Encode=[" + encode + "]" ;
			throw new OgRuntimeException( errMsg,ex );
		}
		finally {
			Closer.ioClose( reader ) ;
			Closer.ioClose( writer ) ;		// 5.7.7.1 (2014/06/13) ioClose は､引数が null なら無視します｡
		}

		return new long[] { lineCnt,charCnt };
	}
}
