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