    001package org.opengion.fukurou.model;
    002
    003import java.io.ByteArrayInputStream;
    004import java.io.ByteArrayOutputStream;
    005import java.io.File;
    006import java.io.FileFilter;
    007import java.io.FileNotFoundException;
    008import java.io.IOException;
    009import java.io.InputStream;
    010import java.net.URI;
    011import java.util.ArrayList;
    012import java.util.List;
    013import java.util.regex.Matcher;
    014import java.util.regex.Pattern;
    015
    016import org.opengion.fukurou.system.Closer;
    017import org.opengion.fukurou.util.StringUtil;
    018
    019/**
    020 * クラウドストレージ対応用の抽象クラスです。
    021 * 各ベンダーのストレージに対応したプラグインを作成する場合はこのクラスを継承してください。
    022 *
    023 *
    024 * @og.group ファイル操作
    025 *
    026 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
    027 * @og.rev 5.10.9.0 (2019/03/01) 変更対応
    028 * @author oota
    029 * @since JDK7.0
    030 */
    031public abstract class CloudFileOperation extends FileOperation {
    032        //* このプログラムのVERSION文字列を設定します。{@VALUE} */
    033        private static final String VERSION = "7.0.2.1 (2019/03/04)" ;
    034        private static final long serialVersionUID = 702120190304L ;
    035
    036        /** バッファサイズ {@value} */
    037        private static final int BUFFER_SIZE = 1024 * 4;
    038        /** パス */
    039        protected final String conPath;
    040        /** バケット名 */
    041        protected final String conBucket;
    042
    043        private static final String UNIMPLEMNTED_ERR="このクラスでは未実装のメソッドです。";
    044        private static final char   FS = '/' ;
    045        // 5.10.12.2 (2019/06/17) 相対パス対応「../」と１つ前のディレクトリ情報を抽出(１つ前が先頭の場合は、/ではなく^)
    046        // 7.2.9.4 (2020/11/20) PMD:Variables that are final and static should be all capitals, 'ptnPreDir' is not all capitals.
    047//      private static final Pattern ptnPreDir = Pattern.compile("(?<=/|^)[^/]+/\\.\\./");
    048        private static final Pattern PTN_PRE_DIR = Pattern.compile("(?<=/|^)[^/]+/\\.\\./");
    049
    050        /**
    051         * コンストラクタ
    052         *
    053         *
    054         * @param bucket バケット名
    055         * @param inPath ファイルパス
    056         */
    057        public CloudFileOperation(final String bucket, final String inPath) {
    058                super(inPath);
    059
    060                this.conPath = editPath(replaceFileSeparetor(inPath));
    061
    062                this.conBucket = bucket;
    063
    064                if (StringUtil.isNull(conBucket)) {
    065                        final String errMsg = "バケット未指定です。hayabusa利用ではシステム変数の「CLOUD_BUCKET」にバケット名を設定して下さい。";
    066                        throw new RuntimeException(errMsg);
    067                }
    068        }
    069
    070        /**
    071         * データ書き込み
    072         *
    073         * InputStreamのデータを書き込みます。
    074         *
    075         * @param is 書き込みデータのInputStream
    076         * @throws IOException IO関連のエラー情報
    077         */
    078        @Override
    079        public abstract void write(InputStream is) throws IOException;
    080
    081        /**
    082         * データ読み込み
    083         *
    084         * データを読み込み、InputStreamを返します。
    085         *
    086         * @return 読み込みデータのInputStream
    087         * @throws FileNotFoundException ファイル非存在エラー情報
    088         */
    089        @Override
    090        public abstract InputStream read() throws FileNotFoundException;
    091
    092        /**
    093         * ファイル削除
    094         *
    095         * ファイルを削除します。
    096         *
    097         * @return 成否フラグ
    098         */
    099        @Override
    100        public abstract boolean delete();
    101
    102        /**
    103         * ファイルコピー
    104         *
    105         * ファイルを指定先にコピーします。
    106         *
    107         * @param afPath コピー先
    108         * @return 成否フラグ
    109         */
    110        @Override
    111        public abstract boolean copy(String afPath);
    112
    113        /**
    114         * ファイルサイズ取得
    115         *
    116         * ファイルサイズを返します。
    117         *
    118         * @return ファイルサイズ
    119         */
    120        @Override
    121        public abstract long length();
    122
    123        /**
    124         * 最終更新時刻取得
    125         *
    126         * 最終更新時刻を返します。
    127         *
    128         * @return 最終更新時刻
    129         */
    130        @Override
    131        public abstract long lastModified();
    132
    133        /**
    134         * ファイル判定
    135         *
    136         * ファイルの場合は、trueを返します。
    137         *
    138         * @return ファイルフラグ
    139         */
    140        @Override
    141        public abstract boolean isFile();
    142
    143        /**
    144         * ディレクトリ判定
    145         *
    146         * ディレクトリの場合は、trueを返します。
    147         *
    148         * @return ディレクトリフラグ
    149         */
    150        @Override
    151        public abstract boolean isDirectory();
    152
    153        /**
    154         * 一覧取得
    155         *
    156         * パスのファイルと、ディレクトリ一覧を取得します。
    157         *
    158         * @param filter ﾌｧｲﾙﾌｨﾙﾀｰ
    159         * @return ファイルとティレクトリ一覧
    160         */
    161        @Override
    162        public abstract File[] listFiles(FileFilter filter);
    163
    164        /**
    165         * 親ディレクトリの取得
    166         *
    167         * 親のディレクトリ情報を返します。
    168         *
    169         * @return 親のディレクトリ
    170         */
    171        @Override
    172        public abstract File getParentFile();
    173
    174        /**
    175         * ファイルパス取得
    176         *
    177         * ファイルパスを取得します。
    178         *
    179         * @return 設定パス
    180         */
    181        @Override
    182        public String getPath() {
    183                return conPath;
    184        }
    185
    186        /**
    187         * 絶対パス取得
    188         *
    189         * 絶対パスを取得します。
    190         *
    191         * @return 絶対パス
    192         */
    193        @Override
    194        public String getAbsolutePath() {
    195                return conPath;
    196        }
    197
    198        /**
    199         * ファイル名取得
    200         *
    201         * ファイル名を取得します。
    202         *
    203         * @return 名称
    204         */
    205        @Override
    206        public String getName() {
    207                return drawName(conPath);
    208        }
    209
    210        /**
    211         * 親のパス取得
    212         *
    213         * 親のパスを取得します。
    214         *
    215         * @return 親のパス
    216         */
    217        @Override
    218        public String getParent() {
    219                return drawParent(conPath);
    220        }
    221
    222        /**
    223         * ファイル移動
    224         *
    225         * ファイルを指定先に移動します。
    226         *
    227         * @param afPath 移動先
    228         * @return 成否フラグ
    229         */
    230        @Override
    231        public boolean move(final String afPath) {
    232                boolean flgRtn = false;
    233
    234                flgRtn = copy(afPath);
    235                if (flgRtn) {
    236                        flgRtn = delete();
    237                }
    238
    239                return flgRtn;
    240        }
    241
    242        /**
    243         * 存在チェック
    244         *
    245         * 存在する場合は、trueを返します。
    246         *
    247         * @return 存在フラグ
    248         */
    249        @Override
    250        public boolean exists() {
    251                return isDirectory() | isFile();
    252        }
    253
    254        /**
    255         * ディレクトリの作成
    256         *
    257         * ※１つのディレクトリのみ作成します。
    258         * クラウドストレージにはディレクトリの概念が無いため、
    259         * 作成は行わず、trueを返します。
    260         *
    261         * @return 成否フラグ
    262         */
    263        @Override
    264        public boolean mkdir() {
    265                return true;
    266        }
    267
    268        /**
    269         * ディレクトリの作成(複数)
    270         *
    271         * ※複数のディレクトリを作成します。
    272         * クラウドストレージにはディレクトリの概念が無いため、
    273         * 作成は行わず、trueを返します。
    274         *
    275         *
    276         * @return 成否フラグ
    277         */
    278        @Override
    279        public boolean mkdirs() {
    280                return true;
    281        }
    282
    283        /**
    284         * ファイル名変更
    285         *
    286         * 指定のファイル情報のファイル名に変更します。
    287         *
    288         * @param dest 変更後のファイル情報
    289         * @return 成否フラグ
    290         */
    291        @Override
    292        public boolean renameTo(final File dest) {
    293                return move(dest.getPath());
    294        }
    295
    296        /**
    297         * 書き込み可能フラグ
    298         *
    299         * ※クラウドストレージの場合は、
    300         * 存在すればtrueを返します。
    301         *
    302         * @return 書き込み可能フラグ
    303         */
    304        @Override
    305        public boolean canWrite() {
    306                return exists();
    307        }
    308
    309        /**
    310         * 読み取り可能フラグ
    311         *
    312         * ※クラウドストレージの場合は、
    313         * 存在すればtrueを返します。
    314         *
    315         * @return 読み取り可能フラグ
    316         */
    317        @Override
    318        public boolean canRead() {
    319                return exists();
    320        }
    321
    322        /**
    323         * 隠しファイルフラグ
    324         *
    325         * ※クラウドストレージの場合は、
    326         * 必ずfalseを返します。
    327         *
    328         * @return 隠しファイルフラグ
    329         */
    330        @Override
    331        public boolean isHidden() {
    332                return false;
    333        }
    334
    335        /**
    336         * 新規ファイル作成
    337         *
    338         * 既にファイルが存在しない場合のみ、
    339         * 空のファイルを作成します。
    340         *
    341         * @return 成否フラグ
    342         * @throws IOException ファイル関連エラー情報
    343         */
    344        @Override
    345        public boolean createNewFile() throws IOException {
    346                boolean rtn = false;
    347
    348                if (!exists()) {
    349                        InputStream is = null;
    350                        try {
    351                                is = new ByteArrayInputStream(new byte[0]);
    352                                write(is);
    353                                rtn = true;
    354                        } finally {
    355                                Closer.ioClose(is);
    356                        }
    357                }
    358
    359                return rtn;
    360        }
    361
    362        /**
    363         * 最終更新時刻の更新
    364         *
    365         * 最終更新時刻の更新を行います。
    366         * ※クラウドストレージの場合は、
    367         * 最終更新時刻の更新を行えません。
    368         *
    369         * @param time 更新する最終更新時刻
    370         * @return 成否フラグ
    371         */
    372        @Override
    373        public boolean setLastModified(final long time) {
    374                // クラウドストレージでは、setLastModifiedによる、
    375                // 最終更新時刻の設定はできないので、
    376                // 処理を行わずにtrueを返します。
    377                return true;
    378        }
    379
    380        /**
    381         * カノニカルファイル情報の取得
    382         *
    383         * ※ローカルサーバのみ通常ファイルと、
    384         * カノニカルファイルで異なります。
    385         *
    386         * @return カノニカルファイル情報
    387         * @throws IOException ファイル関連エラー情報
    388         */
    389        @Override
    390        public FileOperation getCanonicalFile() throws IOException {
    391                return this;
    392        }
    393
    394        /**
    395         * toString
    396         *
    397         * パスを返します。
    398         *
    399         * @return ファイルパス
    400         */
    401        @Override
    402        public String toString() {
    403                return conPath;
    404        }
    405
    406        /** 共通関数 **/
    407        /**
    408         * ファイルパスの編集
    409         *
    410         * パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。
    411         *
    412         * @og.rev 5.10.12.2 (2019/06/17) 相対パス対応
    413         *
    414         * @param path ファイルパス
    415         * @return 変更後パス
    416         */
    417        protected String editPath(final String path) {
    418                if (StringUtil.isNull(path)) {
    419                        return "";
    420                }
    421                String rtn = path;
    422
    423                // 「//+」は「/」に置換
    424                rtn = rtn.replaceAll("//+", "/");
    425                // 先頭が「/」の場合は除去
    426//              if ("/".equals(rtn.substring(0, 1))) {
    427                if( FS == rtn.charAt(0) ) {
    428                        rtn = rtn.substring(1);
    429                }
    430                // 後尾の「.」は除去
    431                rtn = rTrim(rtn, '.');
    432                // 後尾の「/」は除去
    433                rtn = rTrim(rtn, FS);
    434
    435                // 5.10.12.2 (2019/06/17)
    436                // 「../」の文字列は１つ上のディレクトリに変換を行います。
    437                Matcher mtc = PTN_PRE_DIR.matcher(rtn);
    438
    439                // 「../」が無くなるまで、１つずづ変換します。
    440                while(mtc.find()) {
    441                        rtn = mtc.replaceFirst("");
    442                        mtc = PTN_PRE_DIR.matcher(rtn);
    443                }
    444
    445                return rtn;
    446        }
    447
    448        /**
    449         * 親のパスを抽出
    450         *
    451         * キーから親のパスを抽出します。
    452         *
    453         * @param key キー
    454         * @return 親のパス
    455         */
    456        protected String drawParent(final String key) {
    457                final int k = key.lastIndexOf(FS);
    458
    459                String rtn = "";
    460                if (k > 0) {
    461                        rtn = key.substring(0, key.lastIndexOf(FS));
    462                }
    463                if ("/".equals(File.separator)) {
    464                        rtn = File.separator + rtn;
    465                }
    466
    467                return rtn;
    468        }
    469
    470        /**
    471         * 名称の抽出
    472         *
    473         * 引数のkeyから名称を抽出します。
    474         *
    475         * @param key キー(パス)
    476         * @return 名称
    477         */
    478        protected String drawName(final String key) {
    479                final int k = key.lastIndexOf(FS);
    480
    481                String rtn = key;
    482                if (k > 0) {
    483                        rtn = key.substring(key.lastIndexOf(FS) + 1);
    484                }
    485                return rtn;
    486        }
    487
    488        /**
    489         * ディレクトリ用のパス編集
    490         *
    491         * 後尾に「/」がない場合は、付与します。
    492         *
    493         * @param path パス
    494         * @return 後尾に「/」ありのパス
    495         */
    496        protected String setDirTail(final String path) {
    497                if (StringUtil.isNull(path)) {
    498                        return path;
    499                }
    500
    501                final StringBuilder sb = new StringBuilder(path);
    502//              if (!"/".equals(path.substring(path.length() - 1))) {
    503                if ( FS != path.charAt(path.length() - 1) ) {
    504                        sb.append(FS);
    505                }
    506                return sb.toString();
    507        }
    508
    509        /**
    510         * 右側トリム処理
    511         *
    512         * 右側の文字が、指定の文字の場合、除去します。
    513         *
    514         * @param str 対象文字列
    515         * @param chr 指定文字
    516         * @return 右側から指定文字を除去後の文字列
    517         */
    518        protected String rTrim(final String str, final char chr) {
    519                String rtn = str;
    520                int trgPos = 0;
    521                for( int i = str.length() - 1; i >= 0; i--) {
    522                        if (str.charAt(i) == chr) {
    523                                trgPos = i;
    524                                // すべて合致した場合は、から文字を返す
    525                                if (trgPos == 0) {
    526                                        rtn = "";
    527                                }
    528                        } else {
    529                                break;
    530                        }
    531                }
    532
    533                if (trgPos > 0) {
    534                        rtn = str.substring(0, trgPos);
    535                }
    536
    537                return rtn;
    538        }
    539
    540        /**
    541         * ファイル区切り文字変換
    542         *
    543         * ファイル区切り文字を変換します。
    544         *
    545         * @param path 変換前文字列
    546         * @return 返還後文字列
    547         */
    548        protected String replaceFileSeparetor(final String path) {
    549                if (StringUtil.isNull(path)) {
    550                        return "";
    551                }
    552
    553                return path.replaceAll("\\\\", "/");
    554        }
    555
    556        /**
    557         * フィルター処理
    558         *
    559         * フィルター処理を行います。
    560         *
    561         * @param list フィルタを行うリスト
    562         * @param filter フィルタ情報
    563         * @return フィルタ後のリスト
    564         */
    565        protected File[] filter(final List<File> list, final FileFilter filter) {
    566                final List<File> files = new ArrayList<File>();
    567                for( final File file : list ) {
    568                        if (filter.accept(file)) {
    569                                files.add(file);
    570                        }
    571                }
    572                return files.toArray(new File[files.size()]);
    573        }
    574
    575        /**
    576         * ストリームの変換処理
    577         *
    578         * InputStreamをbyte[]に変換。
    579         * InputStreamのサイズ計算に利用。
    580         *
    581         * @param is byte配列変換するInputStream
    582         * @return InpusStreamをbyte配列に変換した値
    583         * @throws IOException ファイル関連エラー情報
    584         */
    585        protected byte[] toByteArray(final InputStream is) throws IOException {
    586                final ByteArrayOutputStream output = new ByteArrayOutputStream();
    587                try {
    588                        // 7.2.9.4 (2020/11/20) Avoid variables with short names like b   b → bt , n → no
    589                        final byte[] bt = new byte[BUFFER_SIZE];
    590                        int no = 0;
    591                        while ((no = is.read(bt)) != -1) {
    592                                output.write(bt, 0, no);
    593                        }
    594                        return output.toByteArray();
    595                } finally {
    596                        output.close();
    597                }
    598        }
    599
    600        /**
    601         * ローカル実行フラグ判定
    602         *
    603         * このabstract クラスの継承クラスはクラウド上で実行されるため、
    604         * falseを返します。
    605         *
    606         * @return ローカル実行フラグ
    607         */
    608        @Override
    609        public boolean isLocal() {
    610                return false;
    611        }
    612
    613        /** java.io.Fileに実装されており、クラウド用ファイルクラスに未実装のメソッドの対応 */
    614        /**
    615         * canExecuteの実行
    616         *
    617         * クラウド側では未実装のメソッドです。
    618         *
    619         * @return フラグ
    620         */
    621        @Override
    622        public boolean canExecute() {
    623                throw new RuntimeException(UNIMPLEMNTED_ERR);
    624        }
    625
    626        /**
    627         * deleteOnExitの実行
    628         *
    629         * クラウド側では未実装のメソッドです。
    630         *
    631         */
    632        @Override
    633        public void deleteOnExit() {
    634                throw new RuntimeException(UNIMPLEMNTED_ERR);
    635        }
    636
    637        /**
    638         * getAbsoluteFileの実行
    639         *
    640         * クラウド側では未実装のメソッドです。
    641         *
    642         * @return Fileｵﾌﾞｼﾞｪｸﾄ
    643         */
    644        @Override
    645        public File getAbsoluteFile() {
    646                throw new RuntimeException(UNIMPLEMNTED_ERR);
    647        }
    648
    649        /**
    650         * getFreeSpaceの実行
    651         *
    652         * クラウド側では未実装のメソッドです。
    653         *
    654         * @return 数値
    655         */
    656        @Override
    657        public long getFreeSpace() {
    658                throw new RuntimeException(UNIMPLEMNTED_ERR);
    659        }
    660
    661        /**
    662         * getTotalSpaceの実行
    663         *
    664         * クラウド側では未実装のメソッドです。
    665         *
    666         * @return 数値
    667         */
    668        @Override
    669        public long getTotalSpace() {
    670                throw new RuntimeException(UNIMPLEMNTED_ERR);
    671        }
    672
    673        /**
    674         * getUsableSpaceの実行
    675         *
    676         * クラウド側では未実装のメソッドです。
    677         *
    678         * @return 数値
    679         */
    680        @Override
    681        public long getUsableSpace() {
    682                throw new RuntimeException(UNIMPLEMNTED_ERR);
    683        }
    684
    685        /**
    686         * isAbsoluteの実行
    687         *
    688         * クラウド側では未実装のメソッドです。
    689         *
    690         * @return フラグ
    691         */
    692        @Override
    693        public boolean isAbsolute() {
    694                throw new RuntimeException(UNIMPLEMNTED_ERR);
    695        }
    696
    697        /**
    698         * setReadableの実行
    699         *
    700         * クラウド側では未実装のメソッドです。
    701         *
    702         * @param readable フラグ
    703         * @return フラグ
    704         */
    705        @Override
    706        public boolean setReadable(final boolean readable) {
    707                throw new RuntimeException(UNIMPLEMNTED_ERR);
    708        }
    709
    710        /**
    711         * setReadableの実行
    712         *
    713         * クラウド側では未実装のメソッドです。
    714         *
    715         * @param readable フラグ
    716         * @param ownerOnly フラグ
    717         * @return フラグ
    718         */
    719        @Override
    720        public boolean setReadable(final boolean readable, final boolean ownerOnly) {
    721                throw new RuntimeException(UNIMPLEMNTED_ERR);
    722        }
    723
    724        /**
    725         * setWritableの実行
    726         *
    727         * クラウド側では未実装のメソッドです。
    728         *
    729         * @param writable フラグ
    730         * @return フラグ
    731         */
    732        @Override
    733        public boolean setWritable(final boolean writable) {
    734                throw new RuntimeException(UNIMPLEMNTED_ERR);
    735        }
    736
    737        /**
    738         * canExecuteの実行
    739         *
    740         * クラウド側では未実装のメソッドです。
    741         *
    742         * @param writable フラグ
    743         * @param ownerOnly フラグ
    744         * @return フラグ
    745         */
    746        @Override
    747        public boolean setWritable(final boolean writable, final boolean ownerOnly) {
    748                throw new RuntimeException(UNIMPLEMNTED_ERR);
    749        }
    750
    751        /**
    752         * canExecuteの実行
    753         *
    754         * クラウド側では未実装のメソッドです。
    755         *
    756         * @return URI情報
    757         */
    758        @Override
    759        public URI toURI() {
    760                throw new RuntimeException(UNIMPLEMNTED_ERR);
    761        }
    762}