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