/*
 * 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.hayabusa.taglib;

import org.opengion.fukurou.system.OgRuntimeException ;		// 6.4.2.0 (2016/01/29)
import org.opengion.hayabusa.common.HybsSystem;
import org.opengion.hayabusa.common.HybsSystemException;
import org.opengion.fukurou.util.FileUtil;
import org.opengion.fukurou.util.StringUtil ;
import org.opengion.fukurou.util.ToString;						// 6.1.1.0 (2015/01/17)
import org.opengion.fukurou.util.ZipArchive;					// 5.9.2.2 (2015/11/12)
import org.opengion.fukurou.util.ArraySet;						// 6.4.3.4 (2016/03/11)
import org.opengion.fukurou.model.POIUtil;						// 6.2.3.0 (2015/05/01)

import static org.opengion.fukurou.util.StringUtil.nval ;

import java.io.File;
import java.io.IOException;
import java.util.List;											// 6.2.3.0 (2015/05/01)
import java.util.Set;											// 6.4.3.4 (2016/03/11)
import java.util.ArrayList;										// 6.2.3.0 (2015/05/01)
import java.util.Locale ;

import javax.servlet.ServletContext;							// 6.3.6.1 (2015/08/28)

/**
 * 各種アクションを指定して、ファイル関連の操作をおこなうタグです。
 *
 * 各種アクション に応じた振る舞いを行います。
 * 結果については、false の場合は、body 要素を表示して、終了します。
 * これは、BODYにエラーメッセージを書いておくことを想定した作りになっています。

 * 判定結果を反転したい場合は、notEquals 属性を使用してください。また、
 * 結果に応じて、処理を止めたくない場合は、useStop 属性を false に指定することで、
 * 後続処理を実行できます。
 *
 * [各種アクション]
 *       canRead          読み込めるかどうかを判定。
 *       canWrite         変更できるかどうか判定。
 *       createNewFile    空の新しいファイルを不可分 (atomic) に生成。(そのファイルがまだ存在しない場合だけ)
 *       delete           ファイルまたはディレクトリを削除(ディレクトリ階層をすべて削除)。
 *       renameTo         ファイルまたはディレクトリ名を変更。
 *       exists           ファイルが存在するかどうか判定。
 *       isDirectory      ファイルがディレクトリであるかどうか判定。
 *       isFile           ファイルが普通のファイルかどうか判定。
 *       isHidden         ファイルが隠しファイルかどうか判定。
 *       mkdir            ディレクトリを生成。
 *       mkdirs           ディレクトリを複数生成。
 *       read             ファイルを読み込んで、パラメータにセットします(6.2.3.0 (2015/05/01))。
 *       existsLength     ファイルサイズが０Byte以上のファイルが存在するかどうか判定。
 *       copy             ファイルまたはディレクトリをコピー(file1 ⇒ file2 にコピー)。
 *       copyST           ストリームファイルをコピー(file1(アドレス) ⇒ file2 にコピー)。
 *       list             ファイルリストをListオブジェクトにコピーして、パラメータにセットします(6.2.3.0 (2015/05/01))。
 *       zip              ファイルまたはディレクトリをZIPファイルに圧縮します。(file1⇒file2)
 *       timeStamp        ファイルのタイムスタンプを現在時刻に変更します。(file1のみ)
 *
 * @og.formSample
 * ●形式：&lt;og:file action="…" fileURL="…" &gt;･･･&lt;/og:file&gt;
 * ●body：あり(EVAL_BODY_INCLUDE:BODYをインクルードし、{&#064;XXXX} は解析しません)
 *
 * ●Tag定義：
 *   &lt;og:file
 *       action           ○【TAG】アクション(canRead,canWrite,createNewFile,delete,exists,isDirectory,isFile,isHidden,mkdir,mkdirs,renameTo,read,existsLength,copy,copyST,list,zip,timeStamp)を指定します(必須)。
 *       fileURL            【TAG】操作するファイルのディレクトリを指定します (初期値:FILE_URL[=filetemp/])
 *       file1              【TAG】基準となるファイル名を指定します(コマンドの左辺のファイル名です)
 *       file2              【TAG】処理結果となるファイル名を指定します(コマンドの右辺のファイル名です)
 *       notEquals          【TAG】判定結果を反転させるかどうか[true/false]を指定します(初期値:false)
 *       useStop            【TAG】エラー時BODYを処理後に停止するかどうか[true/false]を指定します(初期値:true)
 *       encode             【TAG】ファイルを読み込む(action="read")際のエンコードを指定します(初期値:OS依存文字コード)
 *       caseKey            【TAG】このタグ自体を利用するかどうかの条件キーを指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseVal            【TAG】このタグ自体を利用するかどうかの条件値を指定します(初期値:null) 5.7.7.2 (2014/06/20)
 *       caseNN             【TAG】指定の値が、null/ゼロ文字列 でない場合(Not Null=NN)は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseNull           【TAG】指定の値が、null/ゼロ文字列 の場合は、このタグは使用されます(初期値:判定しない) 5.7.7.2 (2014/06/20)
 *       caseIf             【TAG】指定の値が、true/TRUE文字列の場合は、このタグは使用されます(初期値:判定しない)
 *       debug              【TAG】デバッグ情報を出力するかどうか[true/false]を指定します(初期値:false)
 *   &gt;   ... Body ...
 *   &lt;/og:file&gt;
 *
 * ●使用例
 *    ・ファイルの存在チェック→存在しなければエラーメッセージを表示。
 *        &lt;og:file action="exists" fileURL="N:/CIR/" file1="{&#064;USER.LKISB}/{&#064;USER.LDNO1KAI}.cir/001.sht"&gt;
 *            &lt;og:message lbl="RKE_0157" comment="回路図が存在しません。" /&gt;
 *        &lt;/og:file&gt;
 *
 *    ・N:/Filetemp/にユーザーディレクトリが存在しなければ作成。→失敗した場合エラーメッセージを表示。
 *        &lt;og:file action="mkdir" fileURL="N:/Filetemp/{&#064;USER.ID}" &gt;
 *            &lt;og:message comment="エラーが発生しました。システム管理者に連絡してください。" /&gt;
 *        &lt;/og:file&gt;
 *
 *    ・N:/Filetemp/test.txt ファイルの削除。ファイルが存在しなくても処理を続ける。
 *        &lt;og:file action="delete" fileURL="N:/Filetemp/" file1="test.txt" useStop="false" &gt;
 *            &lt;og:message comment="ファイルは存在しませんでした。" /&gt;
 *        &lt;/og:file&gt;
 *
 * @og.group その他部品
 *
 * @version  4.0
 * @author	 Kazuhiko Hasegawa
 * @since    JDK5.0,
 */
public class FileTag extends CommonTagSupport {
	/** このプログラムのVERSION文字列を設定します。	{@value} */
	private static final String VERSION = "6.8.0.0 (2017/06/02)" ;
	private static final long serialVersionUID = 680020170602L ;

	/** action 引数に渡す事の出来る アクションコマンド  読み込めるかどうか {@value} */
	public static final String ACT_CANREAD	= "canRead" ;
	/** action 引数に渡す事の出来る アクションコマンド  変更できるかどうか {@value} */
	public static final String ACT_CANWRITE    = "canWrite" ;
	/** action 引数に渡す事の出来る アクションコマンド  空の新しいファイルを不可分 (atomic) に生成します (そのファイルがまだ存在しない場合だけ {@value} */
	public static final String ACT_CREATENEWFILE	= "createNewFile" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイルまたはディレクトリを削除{@value}	*/
	public static final String ACT_DELETE	 = "delete" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイルが存在するかどうか {@value} */
	public static final String ACT_EXISTS	 = "exists" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイルがディレクトリであるかどうか{@value}	*/
	public static final String ACT_ISDIRECTORY	  = "isDirectory" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイルが普通のファイルかどうか{@value}	*/
	public static final String ACT_ISFILE	 = "isFile" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイルが隠しファイルかどうか {@value} */
	public static final String ACT_ISHIDDEN    = "isHidden" ;
	/** action 引数に渡す事の出来る アクションコマンド  ディレクトリを生成します。 {@value} */
	public static final String ACT_MKDIR	= "mkdir" ;
	/** action 引数に渡す事の出来る アクションコマンド  ディレクトリを生成します。 {@value} */
	public static final String ACT_MKDIRS	= "mkdirs" ;
	/** action 引数に渡す事の出来る アクションコマンド  ファイル名を変更します。 {@value} */
	public static final String ACT_RENAMETO	= "renameTo" ;		// 3.5.6.5 (2004/08/09)
	/** action 引数に渡す事の出来る アクションコマンド  ファイルを読み込んで表示します。 {@value} */
	public static final String ACT_READ	= "read" ;		// 3.6.0.0 (2004/09/25)
	/** action 引数に渡す事の出来る アクションコマンド  ファイルサイズが０Byte以上のファイルが存在するかどうか判定。 {@value} */
	public static final String ACT_EXISTSLENGTH	 = "existsLength" ;		// 3.8.5.2 (2006/05/31)
	/** action 引数に渡す事の出来る アクションコマンド  ファイルまたはディレクトリをコピーします。 {@value} */
	public static final String ACT_COPY	 = "copy" ;		// 5.3.6.0 (2011/06/01)
	/** action 引数に渡す事の出来る アクションコマンド  ストリームファイルをコピーします。 {@value} */
	public static final String ACT_COPYST = "copyST" ;	// 6.3.6.1 (2015/08/28)
	/** action 引数に渡す事の出来る アクションコマンド  ディレクトリのファイルリストをListオブジェクトにコピーします。 {@value} */
	public static final String ACT_LIST	 = "list" ;		// 6.2.3.0 (2015/05/01)
	/** action 引数に渡す事の出来る アクションコマンド  ZIP圧縮します。 {@value} */
	public static final String ACT_ZIP	 = "zip" ;		// 5.9.2.2 (2015/11/20)
	/** action 引数に渡す事の出来る アクションコマンド  タイムスタンプを現在時刻に変更します。 {@value} */
	public static final String ACT_TIMESTAMP	 = "timeStamp" ;		// 6.7.0.0 (2016/12/28)

//	/** action 引数に渡す事の出来る コマンド リスト  */
//	private static final String[] ACTION_LIST = { ACT_CANREAD , ACT_CANWRITE , ACT_CREATENEWFILE , ACT_DELETE , ACT_EXISTS , ACT_ISDIRECTORY ,
//													ACT_ISFILE , ACT_ISHIDDEN , ACT_MKDIR , ACT_MKDIRS , ACT_RENAMETO , ACT_READ , 
////													ACT_EXISTSLENGTH , ACT_COPY , ACT_LIST };
////													ACT_EXISTSLENGTH , ACT_COPY , ACT_COPYST , ACT_LIST };				// 6.3.6.1 (2015/08/28)
//													ACT_EXISTSLENGTH , ACT_COPY , ACT_COPYST , ACT_LIST , ACT_ZIP };	// 5.9.2.2 (2015/11/20)
	// 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
	private static final Set<String> ACTION_SET = new ArraySet<>(
													ACT_CANREAD , ACT_CANWRITE , ACT_CREATENEWFILE , ACT_DELETE , ACT_EXISTS , ACT_ISDIRECTORY ,
													ACT_ISFILE , ACT_ISHIDDEN , ACT_MKDIR , ACT_MKDIRS , ACT_RENAMETO , ACT_READ , 
													ACT_EXISTSLENGTH , ACT_COPY , ACT_COPYST , ACT_LIST , ACT_ZIP , ACT_TIMESTAMP );

	private String	fileURL 	= HybsSystem.sys( "FILE_URL" );
	private String	file1		= "";
	private String	file2		;
	private String	action		;
	private boolean rtnCode		;

	private boolean notEquals	;			// 3.8.5.2 (2006/05/31) 判定結果を反転させて処理します。
	private boolean useStop		= true;		// 3.8.5.2 (2006/05/31) エラー時BODYを処理後に停止(true)するかどうか

	private String encode		;			// 5.1.9.0 (2010/08/01) READ時のエンコード指定

	/**
	 * デフォルトコンストラクター
	 *
	 * @og.rev 6.4.2.0 (2016/01/29) PMD refactoring. Each class should declare at least one constructor.
	 */
	public FileTag() { super(); }		// これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。

	/**
	 * Taglibの開始タグが見つかったときに処理する doStartTag() を オーバーライドします。
	 *
	 * @og.rev 3.6.0.0 (2004/09/25) file オブジェクトの作成を actionExec 移動
	 * @og.rev 3.8.5.2 (2006/05/31) notEquals追加。 判定結果を反転させて処理します。
	 * @og.rev 5.7.7.2 (2014/06/20) caseKey,caseVal,caseNN,caseNull 属性を追加
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doStartTag() {
		// 5.7.7.2 (2014/06/20) caseKey,caseVal,caseNN,caseNull 属性を追加
		if( useTag() ) {
			try {
				rtnCode = notEquals ^ actionExec( action );		// 3.8.5.2 (2006/05/31) 排他的論理和(XOR)
			}
			catch( final IOException ex ) {
				final String errMsg = "指定のアクションは実行できません。アクションエラー" + CR
								+ "\t  action=["	+ action	+ "]"	+ CR
								+ "\t  fileURL=["	+ fileURL	+ "]"	+ CR
								+ "\t  file1=["		+ file1		+ "]"	+ CR
								+ "\t  file2=["		+ file2		+ "]"	+ CR ;
				throw new HybsSystemException( errMsg,ex );		// 3.5.5.4 (2004/04/15) 引数の並び順変更
			}

			if( rtnCode ) { return SKIP_BODY ; }			// Body を評価しない
			else {			return EVAL_BODY_INCLUDE ; }	// Body インクルード( extends TagSupport 時)
		}

		return SKIP_BODY ;
	}

	/**
	 * Taglibの終了タグが見つかったときに処理する doEndTag() を オーバーライドします。
	 *
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.8.5.2 (2006/05/31) useStop 追加。 エラー時BODYを処理後に停止(true)するかどうか
	 * @og.rev 5.7.7.2 (2014/06/20) caseKey,caseVal,caseNN,caseNull 属性を追加
	 *
	 * @return	後続処理の指示
	 */
	@Override
	public int doEndTag() {
		debugPrint();		// 4.0.0 (2005/02/28)

		// 6.4.1.1 (2016/01/16) PMD refactoring. A method should have only one exit point, and that should be the last statement in the method
		return useTag() && useStop && !rtnCode  ? SKIP_PAGE : EVAL_PAGE ;

//		if( useTag() ) {
//			return ( useStop && !rtnCode ) ? SKIP_PAGE : EVAL_PAGE ;
//		}
//
//		return EVAL_PAGE ;
	}

	/**
	 * タグリブオブジェクトをリリースします。
	 * キャッシュされて再利用されるので、フィールドの初期設定を行います。
	 *
	 * @og.rev 2.0.0.4 (2002/09/27) カスタムタグの release() メソッドを、追加
	 * @og.rev 3.1.1.2 (2003/04/04) Tomcat4.1 対応。release2() を doEndTag()で呼ぶ。
	 * @og.rev 3.6.0.0 (2004/09/24) columns 、tableId 、file 削除
	 * @og.rev 3.8.5.2 (2006/05/31) notEquals 、useStop 追加
	 * @og.rev 5.1.9.0 (2010/08/01) READ時のエンコード指定
	 *
	 */
	@Override
	protected void release2() {
		super.release2();
		fileURL 	= HybsSystem.sys( "FILE_URL" );
		file1		= "";
		file2		= null;
		action		= null;
		rtnCode		= false;
		notEquals	= false;	// 3.8.5.2 (2006/05/31) 判定結果を反転させて処理します。
		useStop		= true;		// 3.8.5.2 (2006/05/31) エラー時BODYを処理後に停止(true)するかどうか
		encode		= null;		// 5.1.9.0 (2010/08/01) READ時のエンコード指定
	}

	/**
	 * アクションを実行します。
	 * アクションは,指定のアクションコマンドに対応する処理を入力データに
	 * 対して行います。
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) ACTION_LIST のチェックを削除
	 * @og.rev 3.6.0.0 (2004/09/25) ACT_read を追加 , file オブジェクトを移動
	 * @og.rev 3.8.5.2 (2006/05/31) existsLength 追加
	 * @og.rev 4.0.0.0 (2007/11/28) メソッドの戻り値をチェックします。
	 * @og.rev 5.1.9.0 (2010/08/01) READ時のエンコード指定
	 * @og.rev 5.3.6.0 (2011/06/01) ACT_COPY 対応
	 * @og.rev 5.7.1.1 (2013/12/13) copy元(file1)のファイルが存在しなければ、エラーにします。
	 * @og.rev 6.2.3.0 (2015/05/01) list アクション追加、delete 処理は、フォルダ階層を丸ごと削除します。
	 * @og.rev 6.3.6.1 (2015/08/28) ACT_COPYST 対応( copyST アクション時に、copy元に、ｱﾄﾞﾚｽ指定できるようにする)。
	 * @og.rev 5.9.2.2 (2015/11/12) ACT_ZIP追加
	 * @og.rev 6.7.0.0 (2016/12/28) ACT_TIMESTAMP追加
	 * @og.rev 6.8.0.0 (2017/06/02) zipFile の親ディレクトリを作成します。
	 *
	 * @param	action アクション (public static final 宣言されている文字列)
	 *
	 * @return	実行後のデータ
	 */
	private boolean actionExec( final String action ) throws IOException {
		// 6.4.1.1 (2016/01/16) PMD refactoring. Avoid if (x != y) ..; else ..;
		if( action == null ) {
			final String errMsg = "アクションが指定されていません。アクション NULL エラー" + CR
							+ "\t  file=["		+ file1		+ "]" 	+ CR
							+ "\t  matches=["	+ file2		+ "]"	+ CR ;
			throw new HybsSystemException( errMsg );
		}

		final String directory = HybsSystem.url2dir( fileURL );
		final File file = new File( StringUtil.urlAppend( directory,file1 ) );

		boolean rtnVal = false;
//		if( action != null ) {
			if( 	 ACT_CANREAD.equalsIgnoreCase(		 action ) ) { rtnVal = file.canRead(); }
			else if( ACT_CANWRITE.equalsIgnoreCase( 	 action ) ) { rtnVal = file.canWrite(); }
			else if( ACT_CREATENEWFILE.equalsIgnoreCase( action ) ) { rtnVal = file.createNewFile(); }
			else if( ACT_DELETE.equalsIgnoreCase(		 action ) ) { rtnVal = FileUtil.deleteFiles( file ); }
			else if( ACT_EXISTS.equalsIgnoreCase(		 action ) ) { rtnVal = file.exists(); }
			else if( ACT_ISDIRECTORY.equalsIgnoreCase(	 action ) ) { rtnVal = file.isDirectory(); }
			else if( ACT_ISFILE.equalsIgnoreCase(		 action ) ) { rtnVal = file.isFile(); }
			else if( ACT_ISHIDDEN.equalsIgnoreCase( 	 action ) ) { rtnVal = file.isHidden(); }
			else if( ACT_MKDIR.equalsIgnoreCase(		 action ) ) {
				if( file.isDirectory() ) { rtnVal = true; }
				else { rtnVal = file.mkdir(); }
			}
			else if( ACT_MKDIRS.equalsIgnoreCase(		 action ) ) { rtnVal = file.mkdirs(); }
			else if( ACT_RENAMETO.equalsIgnoreCase(		 action ) ) {
				if( file2 != null ) {
					final File newFile = new File( StringUtil.urlAppend( directory,file2 ) );
					if( newFile.exists() && !newFile.delete() ) {
						final String errMsg = "所定のファイルを削除できませんでした。[" + newFile + "]" ;
						throw new OgRuntimeException( errMsg );
					}
					rtnVal = file.renameTo( newFile );
				}
			}
			// 3.6.0.0 (2004/09/25) ACT_read を追加
			else if( ACT_READ.equalsIgnoreCase(			 action ) ) {
				if( file.isFile() ) {
					// 6.2.3.0 (2015/05/01)
					String text ;
					if( POIUtil.isPOI( file ) ) {
						text = POIUtil.extractor( file );
					}
					else {
						final String enc = encode == null ? "UTF-8" : encode ;
						text = POIUtil.extractor( file,enc );
					}

					rtnVal = true;
					setRequestAttribute( "read",text );
				}
				else {
					final String errMsg = "ファイルが存在しないか、ファイルではありません。" + CR
							+ "\t  action=["	+ action	+ "]"	+ CR
							+ "\t  fileURL=["	+ fileURL	+ "]"	+ CR
							+ "\t  directory=[" + directory + "]"	+ CR
							+ "\t  file1=["		+ file1		+ "]"	+ CR ;
					throw new HybsSystemException( errMsg );
				}
			}
			// 3.8.5.2 (2006/05/31) ファイルサイズが０Byte以上のファイルが存在するかどうか判定。
			else if( ACT_EXISTSLENGTH.equalsIgnoreCase( action ) ) {
				rtnVal = file.exists() && file.length() > 0L ;
			}
			// 5.3.6.0 (2011/06/01) ファイルコピー対応
			// 6.0.0.1 (2014/04/25) These nested if statements could be combined
			// 6.3.6.1 (2015/08/28) フォルダとファイルの自動判定(FileUtil.copy側で判断する)
			else if( ACT_COPY.equalsIgnoreCase( action ) && file2 != null ) {
				final File newFile = new File( StringUtil.urlAppend( directory,file2 ) );
				if( file.exists() ) {
					// FileUtil.copy 側で、toFile のフォルダを作成します。
					// 6.3.6.1 (2015/08/28) で、fromFile がディレクトリでも処理できるように変更。
					rtnVal = FileUtil.copy( file, newFile );
				}
				else {
					final String errMsg = "copy元(file1)のファイルが存在しません。" + CR
							+ "\t  action=["	+ action	+ "]"	+ CR
							+ "\t  fileURL=["	+ fileURL	+ "]"	+ CR
							+ "\t  directory=[" + directory + "]"	+ CR
							+ "\t  file1=["		+ file1		+ "]" 	+ CR
							+ "\t  file2=["		+ file2		+ "]"	+ CR ;
					throw new HybsSystemException( errMsg );
				}

//				if( file.isFile() ) {
//					// FileUtil.copy 側で、toFile のフォルダを作成します。
//					rtnVal = FileUtil.copy( file, newFile );
//				}
//				else if( file.isDirectory() ) {
//					rtnVal = FileUtil.copyDirectry( file, newFile );
//				}
//				// 5.7.1.1 (2013/12/13) copy元(file1)のファイルが存在しなければ、エラーにします。
//				else if( !file.exists() ) {
//					final String errMsg = "copy元(file1)のファイルが存在しません。"
//							+ CR
//							+ "action=[" + action + "] , "
//							+ "  fileURL=[" + fileURL + "]"
//							+ "  directory=[" + directory + "]"
//							+ "  file1=[" + file1 + "]" 
//							+ "  file2=[" + file2 + "]" ;
//					throw new HybsSystemException( errMsg );
//				}
			}
			// 6.3.6.1 (2015/08/28) copyST アクション時に、copy元に、ｱﾄﾞﾚｽ指定できるようにする。
			else if( ACT_COPYST.equalsIgnoreCase( action ) && file2 != null ) {
				final File newFile = new File( StringUtil.urlAppend( directory,file2 ) );

				// ※ getResourceAsStream は、/gf/jsp/･･･ ではなく、/jsp/･･･ でのアドレス指定で取得します。
				final ServletContext application = pageContext.getServletContext();
				rtnVal = FileUtil.copy( application.getResourceAsStream( file1 ), newFile );

				if( !rtnVal ) {
					final String errMsg = action + " アクションが実行できませんでした。" + CR
							+ "\t  action=["	+ action	+ "]"	+ CR
							+ "\t  fileURL=["	+ fileURL	+ "]"	+ CR
							+ "\t  directory=[" + directory + "]"	+ CR
							+ "\t  file1=["		+ file1		+ "]" 	+ CR
							+ "\t  file2=["		+ file2		+ "]"	+ CR ;
					throw new HybsSystemException( errMsg );
				}
			}
			// 6.2.3.0 (2015/05/01)
			else if( ACT_LIST.equalsIgnoreCase( action ) ) {
				final String[] files = file.isDirectory() ? file.list() : new String[] { file.getName() };
				final List<String> list = new ArrayList<>();

				// 6.3.9.0 (2015/11/06) null になっている可能性がある(findbugs)
				if( files != null ) {
					final boolean use = file2 == null || file2.isEmpty();
					for( final String nm : files ) {
						if( use || nm.matches( file2 ) ) {
							list.add( nm );
						}
					}
				}
//				final List<String> list = new ArrayList<>();
//				final boolean use = file2 == null || file2.isEmpty();
//				for( final String nm : files ) {
//					if( use || nm.matches( file2 ) ) {
//						list.add( nm );
//					}
//				}

				setRequestAttribute( "list",list );
				rtnVal = true;
			}
			// 5.9.2.2 (2015/11/20) zip追加
			else if( ACT_ZIP.equalsIgnoreCase( action ) && file2 != null ) {
				//あるかないかだけ見る
//				if( file2.toLowerCase().indexOf( ".zip" ) < 0 ){ 
				if( file2.toLowerCase(Locale.JAPAN).indexOf( ".zip" ) < 0 ){ 			// 6.4.1.1 (2016/01/16) PMD refactoring. When doing a String.toLowerCase()/toUpperCase() call, use a Locale
					file2 = file2 + ".zip";
				}
				final File zipFile = new File( StringUtil.urlAppend( directory,file2 ) );
				zipFile.getParentFile().mkdirs();										// 6.8.0.0 (2017/06/02) zipFile の親ディレクトリを作成します。
				final List<File> ziplist = ZipArchive.compress( file , zipFile );		// 6.7.0.0 (2016/12/28) 返り値で、正誤判定します。
//				ZipArchive.compress( file , zipFile );									// 6.4.1.1 (2016/01/16) 返り値は未使用
				rtnVal = !ziplist.isEmpty();			// 6.7.0.0 (2016/12/28) true/false の判定を入れます。
			}
			// 6.7.0.0 (2016/12/28) timeStamp追加。タイムスタンプを現在時刻に変更します。
			else if( ACT_TIMESTAMP.equalsIgnoreCase( action ) ) {
				rtnVal = file.setLastModified( System.currentTimeMillis() );
			}
//		}
//		else {
//			final String errMsg = "アクションが指定されていません。アクション NULL エラー" + CR
//	//						+ "\t  action=["	+ action	+ "]"	+ CR
//							+ "\t  file=["		+ file1		+ "]" 	+ CR
//							+ "\t  matches=["	+ file2		+ "]"	+ CR ;
////							+ "  file=[" + file + "] , matches=[" + file2 + "]" ;
//			throw new HybsSystemException( errMsg );
//		}

		return rtnVal;
	}

	/**
	 * 【TAG】アクション(canRead,canWrite,createNewFile,delete,exists,isDirectory,isFile,isHidden,mkdir,mkdirs,renameTo,read,existsLength,copy,copyST,list)を指定します。
	 *
	 * @og.tag
	 * アクションは,HTMLから(get/post)指定されますので,ACT_xxx で設定される
	 * フィールド定数値のいづれかを、指定できます。
	 * 処理の結果が、false の場合は、body 要素を表示して終了します。
	 * useStop 属性と、notEquals 属性によって、上記の振る舞いをけることが可能です。
	 *
	 * canRead          読み込めるかどうかを判定
	 * canWrite         変更できるかどうか判定
	 * createNewFile    空の新しいファイルを不可分 (atomic) に生成。(そのファイルがまだ存在しない場合だけ)
	 * delete           ファイルまたはディレクトリを削除(ディレクトリ階層をすべて削除)
	 * exists           ファイルが存在するかどうか判定
	 * isDirectory      ファイルがディレクトリであるかどうか判定
	 * isFile           ファイルが普通のファイルかどうか判定
	 * isHidden         ファイルが隠しファイルかどうか判定
	 * mkdir            ディレクトリを生成
	 * mkdirs           ディレクトリを複数生成
	 * renameTo         ファイルまたはディレクトリ名を変更
	 * read             ファイルを読み込んで、パラメータにセットします(6.2.3.0 (2015/05/01))
	 * existsLength     ファイルサイズが０Byte以上のファイルが存在するかどうか判定
	 * copy             ファイルまたはディレクトリをコピー(file1 ⇒ file2 にコピー)
	 * copyST           ストリームファイルをコピー(file1(アドレス) ⇒ file2 にコピー) ※1。
	 * list             ファイルリストをListオブジェクトにコピーして、パラメータにセットします(6.2.3.0 (2015/05/01))
	 *
	 * ※1 copyST の file1 は、getResourceAsStream を使用の為、/gf/jsp/･･･ ではなく、/jsp/･･･ でのアドレス指定で取得します。
	 *
	 * @og.rev 3.0.0.0 (2002/12/25) ACTION_LIST のチェックを導入
	 * @og.rev 3.5.6.2 (2004/07/05) 文字列の連結にStringBuilderを使用します。
	 * @og.rev 6.3.4.0 (2015/08/01) エラーメッセージを、変更。
	 * @og.rev 6.3.6.1 (2015/08/28) copyST アクション追加
	 * @og.rev 6.4.3.4 (2016/03/11) String配列 から、Setに置き換えます。
	 *
	 * @param	cmd アクション文字列
	 * @see		<a href="../../../../constant-values.html#org.opengion.hayabusa.taglib.FileTag.ACT_canRead">アクション定数</a>
	 */
	public void setAction( final String cmd ) {
		action = getRequestParameter( cmd );

//		if( ! check( action, ACTION_LIST ) ) {
		if( ! check( action, ACTION_SET ) ) {
			final String errMsg = "指定のアクションは実行できません。アクションエラー"	+ CR
							+ "\t  action=["	+ action	+ "]"	+ CR
//							+ "\t  actionList=" + String.join( ", " , ACTION_LIST ) ;
							+ "\t  actionList=" + String.join( ", " , ACTION_SET ) ;
			throw new HybsSystemException( errMsg );

//			final StringBuilder errMsg = new StringBuilder( BUFFER_MIDDLE )
//				.append( "指定のアクションは実行できません。アクションエラー" )
//				.append( CR )
//				.append( "action=[" ).append( action ).append( "] " )
//				.append( CR );
//
//			for( int i=0; i<ACTION_LIST.length; i++ ) {
//				errMsg.append( " | " ).append( ACTION_LIST[i] );
//			}
//			errMsg.append( " | " );
//			throw new HybsSystemException( errMsg.toString() );
		}
	}

	/**
	 * 【TAG】操作するファイルのディレクトリを指定します
	 *		(初期値:FILE_URL[={@og.value SystemData#FILE_URL}])。
	 *
	 * @og.tag
	 * この属性で指定されるディレクトリのファイルを操作します。
	 * 指定方法は、通常の fileURL 属性と同様に、先頭が、'/' (UNIX) または、２文字目が、
	 * ":" (Windows)の場合は、指定のURLそのままのディレクトリに、そうでない場合は、
	 * (初期値:システム定数のFILE_URL[={@og.value SystemData#FILE_URL}])。
	 *
	 * @og.rev 4.0.0.0 (2005/01/31) urlAppend メソッドの利用
	 * @og.rev 4.0.0.0 (2007/11/20) 指定されたディレクトリ名の最後が"\"or"/"で終わっていない場合に、"/"を付加する。
	 * @og.rev 6.4.2.1 (2016/02/05) URLの最後に、"/" を追加する処理を廃止。
	 *
	 * @param	url ファイルURL
	 * @see		org.opengion.hayabusa.common.SystemData#FILE_URL
	 */
	public void setFileURL( final String url ) {
		final String furl = nval( getRequestParameter( url ),null );
		if( furl != null ) {
//			final char ch = furl.charAt( furl.length()-1 );
//			if( ch != '/' && ch != '\\' ) { furl = furl + "/"; }
			fileURL = StringUtil.urlAppend( fileURL,furl );
		}
	}

	/**
	 * 【TAG】基準となるファイル名を指定します(コマンドの左辺のファイル名です)。
	 *
	 * @og.tag
	 * コマンドの左辺のファイル名です。
	 *
	 * ※ 6.3.6.1 (2015/08/28)
	 *    copyST アクション時に、copy元に、ｱﾄﾞﾚｽ指定できるようにする。
	 *
	 * @param	fname ファイル名１
	 */
	public void setFile1( final String fname ) {
		file1 = nval( getRequestParameter( fname ),file1 );
	}

	/**
	 * 【TAG】処理結果となるファイル名を指定します(コマンドの右辺のファイル名です)。
	 *
	 * @og.tag
	 * コマンドの右辺のファイル名です。
	 * ただし、action="list" の時は、file2 を、フィルターとして使用します。
	 *
	 * @param	fname ファイル名２
	 */
	public void setFile2( final String fname ) {
		file2 = nval( getRequestParameter( fname ),file2 );
	}

	/**
	 * 【TAG】判定結果を反転させるかどうか[true/false]を指定します(初期値:false)。
	 *
	 * @og.tag
	 * 通常の判定結果において、不成立(false)の場合に、BODY を実行します。
	 * 通常の処理結果の正反対の処理を行います。
	 * 初期値は、通常 (true 以外)です。
	 *
	 * @og.rev 3.8.5.2 (2006/05/31) 新規追加
	 *
	 * @param   flag 判定結果反転 [true:反転する/それ以外:通常]
	 */
	public void setNotEquals( final String flag ) {
		notEquals = nval( getRequestParameter( flag ),notEquals );
	}

	/**
	 * 【TAG】エラー時BODYを処理後に停止するかどうか[true/false]を指定します(初期値:true)。
	 *
	 * @og.tag
	 * 処理結果などに応じて、以下の処理を停止したい場合に、使用します。
	 * 通常は、条件を判定後、false の場合に、BODY部を出力(処理)した後に、
	 * 処理を停止します。(useStop="true")
	 * false を指定すると、判定結果に無関係に、以下の処理を実行します。
	 * 処理は継続したいが、警告表示する場合に、useStop="false" を指定します。
	 * 初期値は、停止する ("true")です。
	 *
	 * @og.rev 3.8.5.2 (2006/05/31) 新規追加
	 *
	 * @param   flag 処理後停止 [true:する/それ以外:しない]
	 */
	public void setUseStop( final String flag ) {
		useStop = nval( getRequestParameter( flag ),useStop );
	}

	/**
	 * 【TAG】ファイルを読み込む(action="read")際のエンコードを指定します(初期値:OS依存文字コード)。
	 *
	 * @og.tag
	 * ファイルを読み込む(action="read")際のエンコードを指定します。
	 * action="read"以外場合には、この属性値は利用されません。
	 * 指定しない場合は、OS依存文字コードで読み込まれます。
	 *
	 * @og.rev 5.1.9.0 (2010/08/01) 新規作成
	 *
	 * @param   enc ファイル読み込みのエンコード
	 */
	public void setEncode( final String enc ) {
		encode = nval( getRequestParameter( enc ),encode );
	}

	/**
	 * このオブジェクトの文字列表現を返します。
	 * 基本的にデバッグ目的に使用します。
	 *
	 * @return このクラスの文字列表現
	 * @og.rtnNotNull
	 */
	@Override
	public String toString() {
		return ToString.title( this.getClass().getName() )
				.println( "VERSION"		,VERSION	)
				.println( "fileURL"		,fileURL	)
				.println( "file1"		,file1		)
				.println( "file2"		,file2		)
				.println( "action"		,action		)
				.println( "rtnCode"		,rtnCode	)
				.println( "notEquals"	,notEquals	)
				.println( "useStop"		,useStop	)
				.println( "Other..."	,getAttributes().getAttribute() )
				.fixForm().toString() ;
	}
}
