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