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