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