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.util.Map ;
020import java.util.LinkedHashMap ;
021// import java.util.Stack;                                                                      // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
022import java.util.Deque;                                                                         // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
023import java.util.ArrayDeque;                                                            // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
024
025import org.opengion.fukurou.system.OgRuntimeException ;         // 6.4.2.0 (2016/01/29)
026import org.opengion.fukurou.util.Argument;
027import org.opengion.fukurou.util.HybsFileFilter;
028import org.opengion.fukurou.system.LogWriter;
029
030/**
031 * Process_FileSearch は、指定のフォルダ以下のファイルを一覧する、FirstProcess
032 * インターフェースと、ChainProcess インターフェースの実装クラスです。
033 *
034 * Fileリストは、FileLineModel オブジェクトを使用します。
035 * ファイル属性(Level,File,Length,Modify,LineCnt,Biko,MD5) と、
036 * オプションで、FILEPATH,ADDRESS,FILENAME 属性を文字列で準備できます。
037 *
038 * 指定の条件に合致するファイルを検索し、LineModel のサブクラスである、
039 * FileLineModel オブジェクトを作成して、下流に渡します。
040 * FileLineModel オブジェクトには、ファイル属性(Level,File,Length,Modify)
041 * が設定されます。
042 *
043 * 引数文字列中にスペースを含む場合は、ダブルコーテーション("") で括って下さい。
044 * 引数文字列の 『=』 の前後には、スペースは挟めません。必ず、-key=value の様に
045 * 繋げてください。
046 *
047 * ※ 6.3.1.1 (2015/07/10)
048 *    ignoreCase の導入と同時に、初期値を、Grep , GrepChange , GrepChangeExcel と『異なる』
049 *    true:区別しない にしました。
050 *    これは、混乱する可能性が高いのですが、ファイル関連の検索において、通常は、大文字小文字の
051 *    区別なしで検索するのが多く、Grep の区別するケースとは異なると判断しました。
052 *    実際は、ignoreCase 属性を毎回記述したくないというのが、本当の所です。
053 *
054 * ※ 6.4.0.2 (2015/12/11)
055 *    prefix,suffix,instr,equals に、("|"で複数指定可) の説明をJavaDocに追加。
056 *    useDIR 属性に、onlyパラメータを追加[false:File/true:File+Dir/only:Dir]。
057 * ※ 7.2.6.0 (2020/06/30)
058 *    useDIR 属性は廃止。
059 *
060 * @og.formSample
061 *  Process_FileSearch -start=d:/ -suffix=jsp
062 *
063 *     -start=開始フォルダ         :検索を開始するフォルダ
064 *   [ -ignoreCase=[true/false]  ] :検索時に大文字小文字を区別しないかどうか(初期値:区別しない[true])
065 *   [ -prefix=接頭辞            ] :File・・・・,View・・・・,など、指定の接頭辞で始まるファイルを検索("|"で複数指定可)
066 *   [ -unprefix=不接頭辞        ] :File・・・・,View・・・・,など、指定の接頭辞で始まらないファイルを検索("|"で複数指定可)
067 *   [ -preDir=接頭辞フォルダ       ] :(追加:7.2.6.0 (2020/06/30))File・・・・,View・・・・,など、指定の接頭辞で始まるフォルダを検索("|"で複数指定可)                     // 6.4.3.2 (2016/02/19)
068 *   [ -unpreDir=不接頭辞フォルダ   ] :(追加:7.2.6.0 (2020/06/30))File・・・・,View・・・・,など、指定の接頭辞で始まらないフォルダを検索("|"で複数指定可)            // 7.2.6.0 (2020/06/30)
069 *   [ -suffix=接尾辞            ] :.txt|.java|.jsp.... など、指定の接尾辞で終わるファイルを検索("|"で複数指定可)
070 *   [ -unsuffix=不接尾辞        ] :.txt|.java|.jsp.... など、指定の接尾辞で終わらないファイルを検索("|"で複数指定可)
071 *   [ -instr=部分文字列         ] :ファイル名と一致する部分文字列を指定("|"で複数指定可)
072 *   [ -uninstr=不部分文字列     ] :ファイル名と一致しな部分文字列を指定("|"で複数指定可)
073 *   [ -instrDir=部分文字列      ] :(追加:7.2.6.0 (2020/06/30))フォルダと一致する部分文字列を指定("|"で複数指定可)                                                                    // 7.2.6.0 (2020/06/30)
074 *   [ -uninstrDir=不部分文字列  ] :(追加:7.2.6.0 (2020/06/30))フォルダと一致しな部分文字列を指定("|"で複数指定可)                                                                     // 7.2.6.0 (2020/06/30)
075 *   [ -equals=一致         廃止 ] :ファイル名と一致する文字列を指定("|"で複数指定可)    7.2.7.0 (2020/08/07) 廃止
076 *   [ -notequals=不一致    廃止 ] :ファイル名と一致しない文字列を指定("|"で複数指定可)  7.2.7.0 (2020/08/07) 廃止
077 *   [ -fileEquals=一致          ] :ファイル名と一致する文字列を指定("|"で複数指定可)    7.2.7.0 (2020/08/07) 追加
078 *   [ -unFileEquals=不一致      ] :ファイル名と一致しない文字列を指定("|"で複数指定可)  7.2.7.0 (2020/08/07) 追加
079 *   [ -match=正規表現           ] :ファイル名と一致する正規表現を指定
080 *   [ -unmatch=正規表現         ] :ファイル名と一致しない正規表現を指定
081 *   [ -matchDir=正規表現        ] :(追加:7.2.6.0 (2020/06/30))フォルダと一致する正規表現を指定                                                                                                               // 7.2.6.0 (2020/06/30)
082 *   [ -unmatchDir=不一致フォルダ   ] :(追加:7.2.6.0 (2020/06/30))フォルダと一致しない正規表現を指定                                                                                                             // 7.2.6.0 (2020/06/30)
083 *   [ -modify=YYYYMMDD          ] :指定日付け以降に変更されたファイルを検索
084 *             YYYYMMDD   : YYYYMMDD 形式での指定日の 00:00:00 を基準時刻
085 *             TODAY      : 実行日の 00:00:00 を基準時刻
086 *             YESTERDAY  : 実行日前日の 00:00:00 を基準時刻
087 *             LAST_WEEK  : 実行日の先週(7日前) 00:00:00 を基準時刻
088 *             MONTH      : 実行月の 1日 00:00:00 を基準時刻
089 *             LAST_MONTH : 実行前月の 同日 00:00:00 を基準時刻
090 *             LAST_YEAR  : 実行前年の 同月同日 00:00:00 を基準時刻
091 *   [ -unmodify=YYYYMMDD        ] :(追加:7.2.6.0 (2020/06/30))指定日付け以前に変更されたファイルを検索(形式はmodifyと同一)
092 *   [ -useDIR=[false/true/only] ] :(廃止:7.2.6.0 (2020/06/30))判定をファイルだけでなく、ディレクトリでも行うかどうかを指定[false:File/true:File+Dir/only:Dir](初期値:false)
093 *   [ -larger=サイズ(Byte)      ] :ファイルの大きさが指定のバイト数と同じか大きいファイルを検索
094 *   [ -smaller=サイズ(Byte)     ] :ファイルの大きさが指定のバイト数より小さいファイルを検索
095 *   [ -isHidden=[true/false]    ] :true:HIDDENファイルのみ検索/false:NORMALファイルのみ検索(初期値:null)
096 *   [ -maxLevel=最大階層数      ] :ディレクトリの階層を下がる最大数(初期値:256)
097 *   [ -useLineCnt=行数計算      ] :ファイルの行数をカウントするかどうかを指定(初期値:false)
098 *   [ -useMD5=MD5計算値         ] :ファイルのMD5計算を行うかどうかを指定(初期値:false)
099 *   [ -useOmitCmnt=[false/true] ] :コメント部分を削除した行数と文字数計算を行うかどうかを指定(初期値:false)
100 *   [ -useFilePath=[false/true] ] :FILEPATH,ADDRESS,FILENAME 属性を文字列で準備します(初期値:false)
101 *   [ -modifyForm=日付フォーマット    ] :MODIFY 属性のDate型から文字列に変換するフォーマットを指定します(初期値:null)
102 *   [ -encode=エンコード名      ] :コメント削除時の文字数計算で利用するファイルのエンコード(初期値:JISAutoDetect)
103 *   [ -inPath=入力共通パス      ] :BIKO作成用のファイルパスから削除する部分(文字数のみ)
104 *   [ -outPath=出力追加パス     ] :BIKO作成用のファイルパスに追加する部分
105 *   [ -errAbend=[true/false]    ] :異常発生時に、処理を中断(true)するか、継続(false)するかを指定する(初期値:true[中断する])
106 *   [ -display=[false/true]     ] :trueは、検索状況を表示します(初期値:false)
107 *   [ -debug=[false/true]       ] :デバッグ情報を標準出力に表示する(true)かしない(false)か(初期値:false[表示しない])
108 *
109 * @version  4.0
110 * @author   Kazuhiko Hasegawa
111 * @since    JDK5.0,
112 */
113public class Process_FileSearch extends AbstractProcess implements FirstProcess , ChainProcess {
114
115//      private Stack<FileListStack> dirs       ;               // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
116        private Deque<FileListStack> dirs       ;               // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
117        private File                    file            ;
118        private HybsFileFilter  filter          ;
119        private FileLineModel   newData         ;
120        private int                             level           = 1;
121
122        private String                  startDir        ;
123        private int                             maxLevel        = 256;
124        private int                             inCount         ;
125        private int                             outCount        ;
126        private int                             inPathLen       ;               // 4.2.3.0 (2008/05/26) BIKO欄用
127        private String                  outPath         ;               // 4.3.1.1 (2008/08/23) BIKO欄用
128        private boolean                 errAbend        = true; // 6.3.1.0 (2015/06/28) 中断する
129        private boolean                 display         ;               // false:表示しない
130        private boolean                 debug           ;               // 5.7.3.0 (2014/02/07) デバッグ情報
131
132        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
133        private static final Map<String,String> MUST_PROPARTY   ;               // [プロパティ]必須チェック用 Map
134        /** staticイニシャライザ後、読み取り専用にするので、ConcurrentHashMap を使用しません。 */
135        private static final Map<String,String> USABLE_PROPARTY ;               // [プロパティ]整合性チェック Map
136
137        static {
138                MUST_PROPARTY = new LinkedHashMap<>();
139                MUST_PROPARTY.put( "start",     "検索を開始するフォルダ(必須)" );
140
141                USABLE_PROPARTY = new LinkedHashMap<>();
142                USABLE_PROPARTY.put( "ignoreCase"       , "大文字小文字を区別しないかどうか(初期値:区別しない[true])" );                // 6.3.1.1 (2015/07/10)
143                USABLE_PROPARTY.put( "preDir"           , "File・・・・,View・・・・,など、指定の接頭辞で始まるフォルダを検索" );           // 6.4.3.2 (2016/02/19)
144                USABLE_PROPARTY.put( "unpreDir"         , "File・・・・,View・・・・,など、指定の接頭辞で始まらないフォルダを検索" ); // 7.2.6.0 (2020/06/30)
145                USABLE_PROPARTY.put( "prefix"           , "File・・・・,View・・・・,など、指定の接頭辞で始まるファイルを検索" );
146                USABLE_PROPARTY.put( "unprefix"         , "File・・・・,View・・・・,など、指定の接頭辞で始まらないファイルを検索" );
147                USABLE_PROPARTY.put( "suffix"           , ".txt|.java|.jsp.... など、指定の接尾辞で終わるファイルを検索" );
148                USABLE_PROPARTY.put( "unsuffix"         , ".txt|.java|.jsp.... など、指定の接尾辞で終わらないファイルを検索" );
149                USABLE_PROPARTY.put( "instr"            , "ファイル名と一致する部分文字列を指定" );
150                USABLE_PROPARTY.put( "uninstr"          , "ファイル名と一致しない部分文字列を指定" );
151                USABLE_PROPARTY.put( "instrDir"         , "フォルダと一致する部分文字列を指定" );                                                                // 7.2.6.0 (2020/06/30)
152                USABLE_PROPARTY.put( "uninstrDir"       , "フォルダと一致しない部分文字列を指定" );                                                               // 7.2.6.0 (2020/06/30)
153//              USABLE_PROPARTY.put( "equals"           , "ファイル名と一致する文字列を指定" );                                                                 // 7.2.7.0 (2020/08/07) 廃止
154//              USABLE_PROPARTY.put( "notequals"        , "ファイル名と一致しない文字列を指定" );                                                                // 7.2.7.0 (2020/08/07) 廃止
155                USABLE_PROPARTY.put( "fileEquals"       , "ファイル名と一致する文字列を指定" );                                                                 // 7.2.7.0 (2020/08/07) 追加
156                USABLE_PROPARTY.put( "unFileEquals"     , "ファイル名と一致しない文字列を指定" );                                                                // 7.2.7.0 (2020/08/07) 追加
157                USABLE_PROPARTY.put( "match"            , "ファイル名と一致する正規表現を指定" );
158                USABLE_PROPARTY.put( "unmatch"          , "ファイル名と一致しない正規表現を指定" );
159                USABLE_PROPARTY.put( "matchDir"         , "フォルダと一致する正規表現を指定" );                                                                 // 7.2.6.0 (2020/06/30)
160                USABLE_PROPARTY.put( "unmatchDir"       , "フォルダと一致しない正規表現を指定" );                                                                // 7.2.6.0 (2020/06/30)
161                USABLE_PROPARTY.put( "modify"           , "指定日付け以降に変更されたファイルを検索" +
162                                                                                CR + "YYYYMMDD   : YYYYMMDD 形式での指定日の 00:00:00 を基準時刻" +
163                                                                                CR + "TODAY      : 実行日の 00:00:00 を基準時刻" +
164                                                                                CR + "YESTERDAY  : 実行日前日の 00:00:00 を基準時刻" +
165                                                                                CR + "LAST_WEEK  : 実行日の先週(7日前) 00:00:00 を基準時刻" +
166                                                                                CR + "MONTH      : 実行月の 1日 00:00:00 を基準時刻" +
167                                                                                CR + "LAST_MONTH : 実行前月の 同日 00:00:00 を基準時刻" +
168                                                                                CR + "LAST_YEAR  : 実行前年の 同月同日 00:00:00 を基準時刻"
169                );
170                USABLE_PROPARTY.put( "unmodify"         , "指定日付け以前に変更されたファイルを検索" );                                                     // 7.2.6.0 (2020/06/30)
171//              USABLE_PROPARTY.put( "useDIR"           , "判定をファイルだけでなく、ディレクトリでも行うかどうかを指定[false:File/true:File+Dir/only:Dir](初期値:false)" );     // 7.2.6.0 (2020/06/30) 廃止
172                USABLE_PROPARTY.put( "larger"           , "ファイルの大きさが指定のバイト数と同じか大きいファイルを検索" );
173                USABLE_PROPARTY.put( "smaller"          , "ファイルの大きさが指定のバイト数より小さいファイルを検索" );
174                USABLE_PROPARTY.put( "isHidden"         , "true:HIDDENファイルのみ検索/false:NORMALファイルのみ検索(初期値:null)" );
175                USABLE_PROPARTY.put( "maxLevel"         , "ディレクトリの階層を下がる最大数(初期値:256)" );
176                USABLE_PROPARTY.put( "useLineCnt"       , "ファイルの行数をカウントするかどうかを指定(初期値:false)" );
177                USABLE_PROPARTY.put( "useMD5"           , "ファイルのMD5計算を行うかどうかを指定(初期値:false)" );          // 5.7.2.1 (2014/01/17)
178                USABLE_PROPARTY.put( "useOmitCmnt"      , "コメント部分を削除した行数と文字数計算を行うかどうかを指定(初期値:false)" );         // 5.7.4.0 (2014/03/07)
179                USABLE_PROPARTY.put( "useFilePath"      , "FILEPATH,ADDRESS,FILENAME 属性を文字列で準備します(初期値:false)" );                                // 6.3.1.0 (2015/06/28)
180                USABLE_PROPARTY.put( "modifyForm"       , "MODIFY 属性のDate型から文字列に変換するフォーマットを指定します(初期値:null)" );  // 6.3.1.1 (2015/07/10)
181                USABLE_PROPARTY.put( "encode"           , "コメント削除時の文字数計算で利用するファイルのエンコード(初期値:JISAutoDetect)" );  // 5.7.4.0 (2014/03/07)
182                USABLE_PROPARTY.put( "inPath"           , "BIKO作成用のファイルパスから削除する部分(文字数のみ)" );
183                USABLE_PROPARTY.put( "outPath"          , "BIKO作成用のファイルパスに追加する部分" );
184                USABLE_PROPARTY.put( "errAbend"         , "異常発生時に、処理を中断(true)するか、継続(false)するか" +
185                                                                                CR + "(初期値:true:中断する)" );                       // 6.3.1.0 (2015/06/28)
186                USABLE_PROPARTY.put( "display"          , "trueは、検索状況を表示します(初期値:false)" );
187                USABLE_PROPARTY.put( "debug"            , "デバッグ情報を標準出力に表示する(true)かしない(false)か" +
188                                                                                CR + "(初期値:false:表示しない)" );                     // 5.7.3.0 (2014/02/07) デバッグ情報
189        }
190
191        /**
192         * デフォルトコンストラクター。
193         * このクラスは、動的作成されます。デフォルトコンストラクターで、
194         * super クラスに対して、必要な初期化を行っておきます。
195         *
196         */
197        public Process_FileSearch() {
198                super( "org.opengion.fukurou.process.Process_FileSearch",MUST_PROPARTY,USABLE_PROPARTY );
199        }
200
201        /**
202         * プロセスの初期化を行います。初めに一度だけ、呼び出されます。
203         * 初期処理(ファイルオープン、DBオープン等)に使用します。
204         *
205         * @og.rev 4.2.2.0 (2008/05/10) 行数カウントの使用有無
206         * @og.rev 4.3.1.1 (2008/08/23) BIKO 欄にoutPath 属性を追加します。
207         * @og.rev 5.1.2.0 (2010/01/01) useDIR 属性を追加します。
208         * @og.rev 5.7.2.1 (2014/01/17) useMD5 属性を追加します。
209         * @og.rev 5.7.4.0 (2014/03/07) useOmitCmnt,encode 属性を追加します。
210         * @og.rev 5.7.4.3 (2014/03/28) larger,smaller属性を文字列に変更
211         * @og.rev 5.7.5.0 (2014/04/04) isHidden属性を追加します。
212         * @og.rev 6.3.1.0 (2015/06/28) errAbend属性追加。
213         * @og.rev 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
214         * @og.rev 6.3.1.1 (2015/07/10) メソッドチェーン化と、大文字/小文字の区別なし(ignoreCase=true)対応
215         * @og.rev 6.3.1.1 (2015/07/10) Modify のフォーマットを、指定可能にします。
216         * @og.rev 6.4.0.2 (2015/12/11) useDIR 属性に、onlyパラメータを追加します[false:File/true:File+Dir/only:Dir]。
217         * @og.rev 7.2.6.0 (2020/06/30) ディレクトリ処理を統一します。
218         * @og.rev 7.2.7.0 (2020/08/07) equals,notequals 廃止、fileEquals,unFileEquals 追加
219         *
220         * @param   paramProcess データベースの接続先情報などを持っているオブジェクト
221         */
222        public void init( final ParamProcess paramProcess ) {
223                final Argument arg = getArgument();
224
225                startDir = arg.getProparty( "start" );
226
227                final String inPath     = arg.getProparty( "inPath" );
228                if( inPath != null ) { inPathLen = inPath.length(); }
229
230                final boolean ignoreCase= arg.getProparty( "ignoreCase" ,true   );      // 6.3.1.1 (2015/07/10)
231                final String preDir             = arg.getProparty( "preDir"             );              // 6.4.3.2 (2016/02/19)
232                final String unpreDir   = arg.getProparty( "unpreDir"   );              // 7.2.6.0 (2020/06/30)
233                final String prefix             = arg.getProparty( "prefix"             );
234                final String unprefix   = arg.getProparty( "unprefix"   );              // 5.1.2.0 (2010/01/01) 追加
235                final String suffix             = arg.getProparty( "suffix"             );
236                final String unsuffix   = arg.getProparty( "unsuffix"   );              // 5.1.2.0 (2010/01/01) 追加
237                final String instr              = arg.getProparty( "instr"              );
238                final String uninstr    = arg.getProparty( "uninstr"    );              // 5.1.2.0 (2010/01/01) 追加
239                final String instrDir   = arg.getProparty( "instrDir"   );              // 7.2.6.0 (2020/06/30)
240                final String uninstrDir = arg.getProparty( "uninstrDir" );              // 7.2.6.0 (2020/06/30)
241//              final String equals             = arg.getProparty( "equals"             );                                                                                      // 7.2.7.0 (2020/08/07) 廃止
242//              final String notequals  = arg.getProparty( "notequals"  );                      // 5.1.2.0 (2010/01/01) 追加    // 7.2.7.0 (2020/08/07) 廃止
243                final String fileEquals         = arg.getProparty( "fileEquals"         );                                                                      // 7.2.7.0 (2020/08/07) 追加
244                final String unFileEquals       = arg.getProparty( "unFileEquals"       );      // 5.1.2.0 (2010/01/01) 追加    // 7.2.7.0 (2020/08/07) 追加
245                final String match              = arg.getProparty( "match"              );
246                final String unmatch    = arg.getProparty( "unmatch"    );
247                final String matchDir   = arg.getProparty( "matchDir"   );              // 7.2.6.0 (2020/06/30)
248                final String unmatchDir = arg.getProparty( "unmatchDir" );              // 7.2.6.0 (2020/06/30)
249                final String modify             = arg.getProparty( "modify"             );
250                final String unmodify   = arg.getProparty( "unmodify"   );              // 7.2.6.0 (2020/06/30)
251                final String larger             = arg.getProparty( "larger"             );
252                final String smaller    = arg.getProparty( "smaller"    );
253                // 8.5.4.2 (2024/01/12) PMD 7.0.0 LinguisticNaming
254//              final String isHidden   = arg.getProparty( "isHidden"   );              // 5.7.5.0 (2014/04/04) 追加
255                final String ifHidden   = arg.getProparty( "isHidden"   );              // 5.7.5.0 (2014/04/04) 追加
256                maxLevel                                = arg.getProparty( "maxLevel",maxLevel);
257                outPath                                 = arg.getProparty( "outPath"    );
258                // 4.2.2.0 (2008/05/10) 行数カウントの使用有無
259                final boolean useLineCnt        = arg.getProparty( "useLineCnt",false );
260                // 5.7.2.1 (2014/01/17) ファイルのMD5の計算有無
261                final boolean useMD5            = arg.getProparty( "useMD5",false );
262                // 5.7.4.0 (2014/03/07) コメント部分を削除した行数と文字数計算を行うかどうか
263                final boolean useOmitCmnt       = arg.getProparty( "useOmitCmnt",false );
264                // 6.3.1.0 (2015/06/28) FILEPATH,ADDRESS,FILENAME 属性追加
265                final boolean useFilePath       = arg.getProparty( "useFilePath",false );
266                // 6.3.1.1 (2015/07/10) MODIFY 属性のDate型から文字列に変換するフォーマットを指定します
267                final String modifyForm = arg.getProparty( "modifyForm",null );
268                // 5.7.4.0 (2014/03/07) コメント削除時の文字数計算で利用するファイルのエンコード(初期値:JISAutoDetect)
269                final String encode                     = arg.getProparty( "encode" , "JISAutoDetect" );
270
271//              // 5.1.2.0 (2010/01/01) 判定をディレクトリでも行うかどうか
272//              // 6.4.0.2 (2015/12/11) useDIR 属性に、onlyパラメータを追加します[false:File/true:File+Dir/only:Dir]。
273//              // 7.2.6.0 (2020/06/30) useDIR属性 廃止
274//              final String useDIR = arg.getProparty( "useDIR","false" );
275
276                errAbend= arg.getProparty( "errAbend"   , errAbend );                   // 6.3.1.0 (2015/06/28) errAbend属性追加
277                display = arg.getProparty( "display"    , display );
278                debug   = arg.getProparty( "debug"              , debug );                              // 5.7.3.0 (2014/02/07) デバッグ情報
279
280                // 6.3.1.1 (2015/07/10) メソッドチェーン化と、大文字/小文字の区別なし(ignoreCase=true)対応
281                // 7.2.6.0 (2020/06/30)
282//              filter = new HybsFileFilter( useDIR,ignoreCase )
283                filter = new HybsFileFilter( ignoreCase )       // 7.2.6.0 (2020/06/30) useDIR属性 廃止
284                        .startsDir(             preDir          )                       // 6.4.3.2 (2016/02/19)
285                        .startsDir(             unpreDir        , true  )       // 7.2.6.0 (2020/06/30)
286                        .startsWith(    prefix          )
287                        .startsWith(    unprefix        , true  )
288                        .endsWith(              suffix          )
289                        .endsWith(              unsuffix        , true  )
290                        .instr(                 instr           )
291                        .instr(                 uninstr         , true  )
292                        .instrDir(              instrDir        )                       // 7.2.6.0 (2020/06/30)
293                        .instrDir(              uninstrDir      , true  )       // 7.2.6.0 (2020/06/30)
294//                      .fileEquals(    equals          )                       // 7.2.7.0 (2020/08/07) 廃止
295//                      .fileEquals(    notequals       , true  )       // 7.2.7.0 (2020/08/07) 廃止
296                        .fileEquals(    fileEquals      )                       // 7.2.7.0 (2020/08/07) 追加
297                        .fileEquals(    unFileEquals, true  )   // 7.2.7.0 (2020/08/07) 追加
298                        .matches(               match           )
299                        .matches(               unmatch         , true  )
300                        .matchDir(              matchDir        )                       // 7.2.6.0 (2020/06/30)
301                        .matchDir(              unmatchDir      , true  )       // 7.2.6.0 (2020/06/30)
302                        .lastModified(  modify          )
303                        .lastModified(  unmodify        , true  )       // 7.2.6.0 (2020/06/30)
304                        .isLarger(              larger          )                       // 5.7.4.3 (2014/03/28) larger,smaller属性を文字列に変更
305                        .isSmaller(             smaller         )
306                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 LinguisticNaming
307//                      .isHidden(              isHidden        );                      // 5.7.5.0 (2014/04/04) 追加
308                        .isHidden(              ifHidden        );                      // 5.7.5.0 (2014/04/04) 追加
309
310                final File tempFile = new File( startDir );
311                if( display ) { println( "start=[" + tempFile + "]" ); }                // 5.7.3.0 (2014/02/07)
312                if( tempFile.isDirectory() ) {
313                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
314//                      dirs = new Stack<>();
315                        dirs = new ArrayDeque<>();
316                        final File[] fileList = tempFile.listFiles( filter );
317                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
318//                      dirs.push( new FileListStack( fileList, level ) );
319                        dirs.addFirst( new FileListStack( fileList, level ) );
320                }
321                else {
322                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
323//                      dirs = new Stack<>();
324                        dirs = new ArrayDeque<>();
325                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 UseShortArrayInitializer
326//                      final File[] fileList = new File[] { tempFile };
327                        final File[] fileList = { tempFile };
328                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
329//                      dirs.push( new FileListStack( fileList, level ) );
330                        dirs.addFirst( new FileListStack( fileList, level ) );
331                }
332
333                newData = new FileLineModel( useLineCnt,useMD5,useOmitCmnt,useFilePath );       // 6.3.1.0 (2015/06/28)
334                newData.setEncode( encode );
335                newData.setModifyForm( modifyForm );                                                                            // 6.3.1.1 (2015/07/10)
336        }
337
338        /**
339         * このデータの処理において、次の処理が出来るかどうかを問い合わせます。
340         * この呼び出し1回毎に、次のデータを取得する準備を行います。
341         *
342         * @og.rev 5.3.8.0 (2011/08/01) 処理中の状態を表示するための println を追加
343         *
344         * @return      処理できる:true / 処理できない:false
345         */
346        @Override       // FirstProcess
347        public boolean next() {
348                // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
349//              while( !dirs.empty() ) {
350                while( !dirs.isEmpty() ) {
351                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
352//                      final FileListStack fStack = dirs.pop();
353                        final FileListStack fStack = dirs.removeFirst();
354
355                        level = fStack.getLevel();
356                        if( level > maxLevel ) { continue; }
357
358                        final File[] fileList = fStack.getFileList();
359                        if( fileList == null ) { continue; }
360
361                        int address = fStack.getAddress();
362                        for( ; address<fileList.length; address++ ) {
363                                inCount++ ;
364                                if( fileList[address].isDirectory() ) {
365                                        if( debug ) { println( "file Add=" + fileList[address].getAbsolutePath() ); }                   // 5.7.3.0 (2014/02/07) デバッグ情報
366                                        final File[] newList = fileList[address].listFiles( filter );
367                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
368//                                      dirs.push( new FileListStack( newList,level+1) );
369                                        dirs.addFirst( new FileListStack( newList,level+1) );
370                                }
371                                else {
372                                        file = fileList[address];
373                                        if( debug ) { println( "file=" + file ); }                      // 5.7.3.0 (2014/02/07) デバッグ情報
374                                        fStack.setAddress( address+1 );
375                                        // 8.5.4.2 (2024/01/12) PMD 7.0.0 ReplaceVectorWithList 対応
376//                                      dirs.push( fStack );
377                                        dirs.addFirst( fStack );
378                                        return true;
379                                }
380                        }
381                }
382                return false;
383        }
384
385        /**
386         * 最初に、行データである LineModel を作成します
387         * FirstProcess は、次々と処理をチェインしていく最初の行データを
388         * 作成して、後続の ChainProcess クラスに処理データを渡します。
389         *
390         * @og.rev 4.2.3.0 (2008/05/26) BIKO 欄に展開ファイル名を記述します。
391         * @og.rev 4.3.1.1 (2008/08/23) BIKO 欄にoutPath 属性を追加します。
392         * @og.rev 6.3.1.0 (2015/06/28) errAbend属性追加。
393         * @og.rev 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
394         *
395         * @param       rowNo   処理中の行番号
396         *
397         * @return      処理変換後のLineModel
398         */
399        @Override       // FirstProcess
400        public LineModel makeLineModel( final int rowNo ) {
401                outCount++ ;
402                // 6.3.1.0 (2015/06/28) 取りあえず、エラーの発生しそうな箇所で対応しておく。
403                try {
404                        newData.setFileVals( level,file );
405                }
406                catch( final RuntimeException ex ) {
407                        // 6.3.1.1 (2015/07/10) throwExceptionメソッドを使用
408                        final String errMsg = "rowNo=[" + rowNo + "] , file =[" + file + "]" ;
409                        throwException( errMsg,ex,errAbend );
410                }
411
412                // 6.3.9.0 (2015/11/06) コンストラクタで初期化されていないフィールドを null チェックなしで利用している(findbugs)
413                if( file == null ) {
414                        final String errMsg = "#next() 実行しておかないと、file が初期化されません。" ;
415                        throw new OgRuntimeException( errMsg );
416                }
417
418                // 4.3.1.1 (2008/08/23)
419                String biko = null;
420                // 4.2.3.0 (2008/05/26) BIKO 欄追加
421                if( inPathLen > 0 ) {
422                        biko = file.getAbsolutePath().substring( inPathLen );
423                }
424
425                if( outPath != null ) {
426                        if( biko == null ) {
427                                biko = outPath + file.getName() ;
428                        }
429                        else {
430                                biko = outPath + biko ;
431                        }
432                }
433                if( biko != null ) {
434                        newData.setBiko( biko );
435                }
436
437                newData.setRowNo( rowNo );
438
439                return newData;
440        }
441
442        /**
443         * 引数の LineModel を処理するメソッドです。
444         * 変換処理後の LineModel を返します。
445         * 後続処理を行わない場合(データのフィルタリングを行う場合)は、
446         * null データを返します。つまり、null データは、後続処理を行わない
447         * フラグの代わりにも使用しています。
448         * なお、変換処理後の LineModel と、オリジナルの LineModel が、
449         * 同一か、コピー(クローン)かは、各処理メソッド内で決めています。
450         * ドキュメントに明記されていない場合は、副作用が問題になる場合は、
451         * 各処理ごとに自分でコピー(クローン)して下さい。
452         *
453         * @param       data    オリジナルのLineModel
454         *
455         * @return      処理変換後のLineModel
456         */
457        @Override       // ChainProcess
458        public LineModel action( final LineModel data ) {
459
460                final FileLineModel fileData ;
461                if( data instanceof FileLineModel ) {
462                        fileData = (FileLineModel)data ;
463                }
464                else {
465                        // これは、プログラマーの問題なので、errAbend 対象外
466                        final String errMsg = "データが FileLineModel オブジェクトではありません。" + CR ;
467                        throw new OgRuntimeException( errMsg );
468                }
469
470                if( debug ) { println( "Before:" + data.dataLine() ); }         // 5.1.2.0 (2010/01/01) display の条件変更
471
472                final File inFile = fileData.getFile() ;
473                final File[] fileList = inFile.listFiles( filter );
474
475                LineModel rtn = null;
476                if( fileList != null && fileList.length > 0 ) {
477                        rtn = data;
478                }
479
480                if( display && rtn != null ) { println( rtn.dataLine() ); }             // 5.1.2.0 (2010/01/01) display の条件変更
481                return rtn ;
482        }
483
484        /**
485         * プロセスの終了を行います。最後に一度だけ、呼び出されます。
486         * 終了処理(ファイルクローズ、DBクローズ等)に使用します。
487         *
488         * @param   isOK トータルで、OKだったかどうか[true:成功/false:失敗]
489         */
490        public void end( final boolean isOK ) {
491                dirs            = null;
492                file            = null;
493                filter          = null;
494                newData         = null;
495        }
496
497        /**
498         * プロセスの処理結果のレポート表現を返します。
499         * 処理プログラム名、入力件数、出力件数などの情報です。
500         * この文字列をそのまま、標準出力に出すことで、結果レポートと出来るような
501         * 形式で出してください。
502         *
503         * @return   処理結果のレポート
504         */
505        public String report() {
506                // 7.2.9.5 (2020/11/28) PMD:Consider simply returning the value vs storing it in local variable 'XXXX'
507                return "[" + getClass().getName() + "]" + CR
508//              final String report = "[" + getClass().getName() + "]" + CR
509                                + TAB + "Start Folder : " + startDir  + CR
510                                + TAB + "Search Count : " + inCount   + CR
511                                + TAB + "Output Count : " + outCount ;
512
513//              return report ;
514        }
515
516        /**
517         * このクラスの使用方法を返します。
518         *
519         * @return      このクラスの使用方法
520         * @og.rtnNotNull
521         */
522        public String usage() {
523                final StringBuilder buf = new StringBuilder( 3000 )
524                        .append( "Process_FileSearch は、指定のフォルダ以下のファイルを一覧する、FirstProcess"        ).append( CR )
525                        .append( "インターフェースと、ChainProcess インターフェースの実装クラスです。"                     ).append( CR )
526                        .append( CR )
527                        .append( "指定の条件に合致するファイルを検索し、ファイル属性(Level,File,Length,Modify)"  ).append( CR )
528                        .append( "を元に、LineModelを作成し、下流に渡します。"                                                                   ).append( CR )
529                        .append( CR )
530//                      .append( "引数文字列中に空白を含む場合は、ダブルコーテーション(\"\") で括って下さい。"    ).append( CR )
531//                      .append( "引数文字列の 『=』 の前後には、空白は挟めません。必ず、-key=value の様に"          ).append( CR )
532//                      .append( "繋げてください。"                                                                                                                             ).append( CR )
533                        .append( PROCESS_PARAM_USAGE )  // 8.5.6.1 (2024/03/29) 継承元使用
534                        .append( CR )
535                        .append( "  -start=開始フォルダ    :検索を開始するフォルダ"                                                              ).append( CR )
536                        .append( "[ -ignoreCase=true/false]:大文字小文字を区別しないかどうか(初期値:しない[true])" ).append( CR )
537                        .append( "[ -preDir=接頭辞フォルダ   ]:File・・・・,View・・・・,などの接頭辞で始まるフォルダ"              ).append( CR )                  // 6.4.3.2 (2016/02/19)
538                        .append( "[ -prefix=接頭辞        ]:File・・・・,View・・・・,などの接頭辞で始まるファイル"              ).append( CR )
539                        .append( "[ -unprefix=不接頭辞    ]:File・・・・,View・・・・,などの接頭辞で始まらないファイル"     ).append( CR )
540                        .append( "[ -suffix=接尾辞        ]:.txt|.java|.jsp.... などの接尾辞で終わるファイル"            ).append( CR )
541                        .append( "[ -unsuffix=不接尾辞    ]:.txt|.java|.jsp.... などの接尾辞で終わらないファイル"   ).append( CR )
542                        .append( "[ -instr=部分文字列     ]:ファイル名と一致する部分文字列"                                         ).append( CR )
543                        .append( "[ -uninstr=不部分文字列 ]:ファイル名と一致しな部分文字列"                                          ).append( CR )
544                        .append( "[ -instrDir=部分文字列  ]:フォルダ名と一致する部分文字列"                                         ).append( CR )
545                        .append( "[ -uninstrDir=不部分文字列 ]:フォルダ名と一致しな部分文字列"                                               ).append( CR )
546//                      .append( "[ -equals=一致          ]:ファイル名と一致する文字列"                                                        ).append( CR )                  // 7.2.7.0 (2020/08/07) 廃止
547//                      .append( "[ -notequals=不一致     ]:ファイル名と一致しない文字列"                                                        ).append( CR )                  // 7.2.7.0 (2020/08/07) 廃止
548                        .append( "[ -fileEquals=一致      ]:ファイル名と一致する文字列"                                                        ).append( CR )                  // 7.2.7.0 (2020/08/07) 追加
549                        .append( "[ -unFileEquals=不一致  ]:ファイル名と一致しない文字列"                                                        ).append( CR )                  // 7.2.7.0 (2020/08/07) 追加
550                        .append( "[ -match=正規表現       ]:ファイル名と一致する正規表現"                                                 ).append( CR )
551                        .append( "[ -unmatch=正規表現     ]:ファイル名と一致しない正規表現"                                                ).append( CR )
552                        .append( "[ -matchDir=正規表現    ]:フォルダ名と一致する正規表現"                                                 ).append( CR )
553                        .append( "[ -unmatchDir=正規表現  ]:フォルダ名と一致しない正規表現"                                                ).append( CR )
554                        .append( "[ -modify=YYYYMMDD      ]:指定日付け以降に変更されたファイル"                                  ).append( CR )
555                        .append( "          YYYYMMDD   : YYYYMMDD 形式での指定日の 00:00:00 を基準時刻"                              ).append( CR )
556                        .append( "          TODAY      : 実行日の 00:00:00 を基準時刻"                                                           ).append( CR )
557                        .append( "          YESTERDAY  : 実行日前日の 00:00:00 を基準時刻"                                                 ).append( CR )
558                        .append( "          LAST_WEEK  : 実行日の先週(7日前) 00:00:00 を基準時刻"                                    ).append( CR )
559                        .append( "          MONTH      : 実行月の 1日 00:00:00 を基準時刻"                                                        ).append( CR )
560                        .append( "          LAST_MONTH : 実行前月の 同日 00:00:00 を基準時刻"                                               ).append( CR )
561                        .append( "          LAST_YEAR  : 実行前年の 同月同日 00:00:00 を基準時刻"                                     ).append( CR )
562                        .append( "[ -unmodify=YYYYMMDD    ]:指定日付け以前に変更されたファイル"                                  ).append( CR )
563//                      .append( "[ -useDIR=[false/true/only]]:判定をディレクトリ名も含めて行うかどうか[false:File/true:File+Dir/only:Dir](初期値:false)").append( CR )
564                        .append( "[ -larger=サイズ(Byte)  ]:大きさが指定のバイト数と同じか大きいファイル"                        ).append( CR )
565                        .append( "[ -smaller=サイズ(Byte) ]:大きさが指定のバイト数より小さいファイル"                          ).append( CR )
566                        .append( "[ -isHidden=[false/true]]:true:HIDDENのみ検索/false:NORMALのみ検索(初期値:null)" ).append( CR )
567                        .append( "[ -maxLevel=最大階層数  ]:ディレクトリの階層を下がる最大数(初期値:256)"                       ).append( CR )
568                        .append( "[ -useLineCnt=行数計算  ]:ファイルの行数をカウントするかどうか(初期値:false)"  ).append( CR )
569                        .append( "[ -useMD5=MD5計算値     ]:ファイルのMD5計算を行うかどうかを指定(初期値:false)"       ).append( CR )
570                        .append( "[ -useOmitCmnt=[false/true] ]:コメント部分を削除した行数と文字数計算を行うかどうか(初期値:false)"  ).append( CR )
571                        .append( "[ -useFilePath=[false/true] ]:FILEPATH,ADDRESS,FILENAME 属性を準備(初期値:false)" ).append( CR )
572                        .append( "[ -modifyForm=日付フォーマット]:MODIFY 属性のDate型から文字列に変換するフォーマットを指定します(初期値:null)" ).append( CR )
573                        .append( "[ -inPath=入力共通パス  ]:BIKO作成用のファイルパスから削除する文字列"                  ).append( CR )
574                        .append( "[ -outPath=出力追加パス ]:BIKO作成用のファイルパスに追加する文字列"                           ).append( CR )
575                        .append( "[ -display=[false/true] ]:trueは、検索状況を表示(初期値:false)"                                   ).append( CR )
576                        .append( "[ -debug=[false/true]   ]:trueは、デバッグ状況を表示(初期値:false)"                         ).append( CR )
577                        .append( CR ).append( CR )
578                        .append( getArgument().usage() ).append( CR );
579
580                return buf.toString();
581        }
582
583        /**
584         * このクラスは、main メソッドから実行できません。
585         *
586         * @param       args    コマンド引数配列
587         */
588        public static void main( final String[] args ) {
589                LogWriter.log( new Process_FileSearch().usage() );
590        }
591
592        /**
593         * このクラスはファイルをスタックを使用して展開する場合の
594         * 個々の状態を保持する為のクラスです。
595         *
596         * @version  4.0
597         * @author   Kazuhiko Hasegawa
598         * @since    JDK5.0,
599         */
600        private static final class FileListStack {
601                private int address ;
602                private final File[] files;
603                private final int level;
604
605                /**
606                 * コンストラクター
607                 * 初期値を設定します。
608                 * ファイルの配列については、コピーせずそのまま内部配列にセットしています。
609                 *
610                 * @param files ファイルの配列(ファイルリスト)
611                 * @param       level   レベル(指定のstartフォルダからの階層数)
612                 */
613                /* default */ FileListStack( final File[] files,final int level ) {
614                        this.files   = files;
615                        this.address = 0;
616                        this.level   = level;
617                }
618
619                /**
620                 * ファイルリストのアドレスを設定します。
621                 * スタックから取り出した後、配列を前回の続きからサーチする場合に使用します。
622                 *
623                 * @param       address ファイルリストのアドレス
624                 */
625                /* default */ void setAddress( final int address ) {
626                        this.address = address;
627                }
628
629                /**
630                 * ファイルリストのアドレスを取り出します。
631                 *
632                 * @return      ファイルリストのアドレス
633                 */
634                /* default */ int getAddress() {
635                        return address;
636                }
637
638                /**
639                 * ファイルリストを取り出します。
640                 * ファイルの配列については、コピーせずそのまま内部配列を返しています。
641                 *
642                 * @return ファイルリスト配列
643                 */
644                /* default */ File[] getFileList() {
645                        return files;
646                }
647
648                /**
649                 * 階層レベルを取り出します。
650                 *
651                 * @return      レベル
652                 */
653                /* default */ int getLevel() {
654                        return level;
655                }
656        }
657}