001package org.opengion.fukurou.model;
002
003import java.io.ByteArrayInputStream;
004import java.io.ByteArrayOutputStream;
005import java.io.File;
006import java.io.FileNotFoundException;
007import java.io.IOException;
008import java.io.InputStream;
009import java.util.ArrayList;
010import java.util.List;
011
012import org.opengion.fukurou.util.Closer;
013import org.opengion.fukurou.util.HybsFileFilter;
014import org.opengion.fukurou.util.StringUtil;
015
016/**
017 * ファイル操作の抽象クラス
018 * 
019 * 共通の処理等を実装しています。
020 * 
021 * @og.group ファイル操作
022 * 
023 * @og.rev 5.10.8.0 (2019/02/01) 新規作成
024 * @og.auth oota
025 * @since JDK7.0
026 */
027public abstract class AbstractFileOperation implements FileOperation {
028
029        /* クラス定数 */
030        private static final int BUFFER_SIZE = 1024 * 4;
031        /* クラス変数 */
032        // パス
033        protected final String path;
034        // バケット名
035        protected final String bucket;
036
037        /**
038         * コンストラクタ
039         * ローカルサーバ用
040         */
041        public AbstractFileOperation() {
042                path = "";
043                bucket = "";
044        }
045
046        /**
047         * コンストラクタ
048         * クラウドストレージ用
049         * 
050         * @param buket バケット
051         * @param inPath パス
052         */
053        public AbstractFileOperation(final String buket , final String inPath) {
054                path = editPath(replaceFileSeparetor(inPath));
055                this.bucket = buket;
056
057                if (StringUtil.isNull(bucket)) {
058                        String errMsg = "バケット未指定です。hayabusa利用ではシステム変数の「CLOUD_BUCKET」にバケット名を設定して下さい。";
059                        throw new RuntimeException(errMsg);
060                }
061        }
062
063        /**
064         * InputStreamのデータを書き込みます。
065         * 
066         * @param is 書き込みデータのInputStream
067         * @throws IOException
068         */
069        @Override
070        public void write(InputStream is) throws IOException {
071                String errMsg = "このクラスでは実装されていません。write(InputStream is)";
072                throw new UnsupportedOperationException(errMsg);
073        }
074
075        /**
076         * データを読み込み、InputStreamとして、返します。
077         * 
078         * @return 読み込みデータのInputStream
079         * @throws FileNotFoundException
080         */
081        @Override
082        public InputStream read() throws FileNotFoundException {
083                String errMsg = "このクラスでは実装されていません。read()";
084                throw new UnsupportedOperationException(errMsg);
085        }
086
087        /**
088         * ファイルを削除します。
089         * 
090         * @return 成否フラグ
091         */
092        @Override
093        public boolean delete() {
094                String errMsg = "このクラスでは実装されていません。delete()";
095                throw new UnsupportedOperationException(errMsg);
096        }
097
098        /**
099         * ファイルを指定先に、コピーします。
100         * 
101         * @param afPath コピー先
102         * @return 成否フラグ
103         */
104        @Override
105        public boolean copy(String afPath) {
106                String errMsg = "このクラスでは実装されていません。copy(String)";
107                throw new UnsupportedOperationException(errMsg);
108        }
109
110        /**
111         * ファイルを指定先に、移動します。
112         * 
113         * @param afPath 移動先
114         * @return 成否フラグ
115         */
116        @Override
117        public boolean move(String afPath) {
118                boolean flgRtn = false;
119
120                flgRtn = copy(afPath);
121                if (flgRtn) {
122                        flgRtn = delete();
123                }
124
125                return flgRtn;
126        }
127
128        /**
129         * 設定パスを取得します。
130         * 
131         * @return 設定パス
132         */
133        @Override
134        public String getPath() {
135                return path;
136        }
137
138        /**
139         * 絶対パスを取得します。
140         * 
141         * @return 絶対パス
142         */
143        @Override
144        public String getAbsolutePath() {
145                return path;
146        }
147
148        /**
149         * 名称を取得します。
150         * 
151         * @return 名称
152         */
153        @Override
154        public String getName() {
155                return drawName(path);
156        }
157
158        /**
159         * 親のパスを取得します。
160         * 
161         * @return 親のパス
162         */
163        @Override
164        public String getParent() {
165                return drawParent(path);
166        }
167
168        /**
169         * ファイルサイズを返します
170         * 
171         * @return ファイルサイズ
172         */
173        @Override
174        public long length() {
175                // TODO 自動生成されたメソッド・スタブ
176                return 0;
177        }
178
179        /**
180         * 最終更新時刻を取得します。
181         * 
182         * @return 最終更新時刻
183         */
184        @Override
185        public long lastModified() {
186                // TODO 自動生成されたメソッド・スタブ
187                return 0;
188        }
189
190        /**
191         * ファイルの場合は、trueを返します。
192         * 
193         * @return ファイルフラグ
194         */
195        @Override
196        public boolean isFile() {
197                // TODO 自動生成されたメソッド・スタブ
198                return false;
199        }
200
201        /**
202         * ディレクトリの場合は、trueを返します。
203         * 
204         * @return ディレクトリフラグ
205         */
206        @Override
207        public boolean isDirectory() {
208                // TODO 自動生成されたメソッド・スタブ
209                return false;
210        }
211
212        /**
213         * 存在する場合は、trueを返します。
214         * 
215         * @return 存在フラグ
216         */
217        @Override
218        public boolean exists() {
219                return isDirectory() | isFile();
220        }
221
222        /**
223         * パスのファイルとディレクトリ一覧を取得します。
224         * 
225         * @return ファイルとティレクトリ一覧
226         */
227        @Override
228        public FileOperation[] listFiles() {
229                return listFiles(new HybsFileFilter());
230        }
231
232        /**
233         * パスのファイルとディレクトリ一覧を取得して、
234         * 引数でフィルターを行います。
235         * 
236         * @param filter フィルター
237         * @return      ファイルとディレクトリ一覧
238         */
239        @Override
240        public FileOperation[] listFiles(FileOperationFileFilter filter) {
241                String errMsg = "このクラスでは実装されていません。listFiles(FileOperationFileFilter)";
242                throw new UnsupportedOperationException(errMsg);
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         * @return 成否フラグ
267         */
268        @Override
269        public boolean mkdirs() {
270                return true;
271        }
272
273        /**
274         * 指定のファイル情報のファイル名に変更します。
275         * 
276         * @param dest 変更後のファイル情報
277         * @return 成否フラグ
278         */
279        @Override
280        public boolean renameTo(FileOperation dest) {
281                return move(dest.getPath());
282        }
283
284        /**
285         * 親のディレクトリを返します。
286         * 
287         * @return 親のディレクトリ
288         */
289        @Override
290        public FileOperation getParentFile() {
291                return null;
292        }
293
294        /**
295         * 書き込み可能フラグ
296         * 
297         * ※クラウドストレージの場合は、
298         * 必ずtrueを返します。
299         * 
300         * @return 書き込み可能フラグ
301         */
302        @Override
303        public boolean canWrite() {
304                return true;
305        }
306
307        /**
308         * 読み取り可能フラグ
309         * 
310         * ※クラウドストレージの場合は、
311         * 必ずtrueを返します。
312         * 
313         * @return 読み取り可能フラグ
314         */
315        @Override
316        public boolean canRead() {
317                return true;
318        }
319
320        /**
321         * 隠しファイルフラグ
322         * 
323         * ※クラウドストレージの場合は、
324         * 必ずfalseを返します。
325         * 
326         * @return 隠しファイルフラグ
327         */
328        @Override
329        public boolean isHidden() {
330                return false;
331        }
332
333        /**
334         * 新規ファイル作成
335         * 
336         * 既にファイルが存在しない場合のみ、
337         * 空のファイルを作成します。
338         *
339         * @return 成否フラグ
340         * @throws IOException
341         */
342        @Override
343        public boolean createNewFile() throws IOException {
344                boolean rtn = false;
345
346                if (!exists()) {
347                        InputStream is = null;
348                        try {
349                                is = new ByteArrayInputStream(new byte[0]);
350                                write(is);
351                                rtn = true;
352                        } finally {
353                                Closer.ioClose(is);
354                        }
355                }
356
357                return rtn;
358        }
359
360        /**
361         * 最終更新時刻の更新
362         * 
363         * 最終更新時刻の更新を行います。
364         * ※クラウドストレージの場合は、
365         * 最終更新時刻の更新を行えません。
366         * 
367         * @param time 更新する最終更新時刻
368         * @return 成否フラグ
369         */
370        @Override
371        public boolean setLastModified(long time) {
372                // クラウドストレージでは、setLastModifiedによる、
373                // 最終更新時刻の設定はできないので、
374                // 処理を行わずにtrueを返します。
375                return true;
376        }
377
378        /**
379         * カノニカルファイル情報の取得
380         * 
381         * ※ローカルサーバのみ通常ファイルと、
382         * カノニカルファイルで異なります。
383         * 
384         * @return カノニカルファイル情報
385         * @throws IOException
386         */
387        @Override
388        public FileOperation getCanonicalFile() throws IOException {
389                return this;
390        }
391
392        /**
393         * toStringメソッド
394         * パスを返します。
395         */
396        @Override
397        public String toString() {
398                return path;
399        }
400
401        /** 共通関数 **/
402        /**
403         * ファイルパスの編集 2018/05/07 ADD
404         * パスの先頭が「/」の場合は「/」の除去と、「//」を「/」に置換処理の追加。
405         * 
406         * @og.rev 5.9.32.1 (2018/05/11)
407         * @param path
408         * @return 変更後パス
409         */
410        protected String editPath(final String path) {
411                if (StringUtil.isNull(path)) {
412                        return "";
413                }
414
415                String rtn = path;
416                // 先頭が「/」の場合は除去
417                if ("/".equals(rtn.substring(0, 1))) {
418                        rtn = rtn.substring(1);
419                }
420                // 「//」は「/」に置換
421                rtn = rtn.replaceAll("//", "/");
422                // 後尾の「.」は除去
423                rtn = rTrim(rtn, '.');
424                // 後尾の「/」は除去
425                rtn = rTrim(rtn, '/');
426
427                return rtn;
428        }
429
430        /**
431         * キーから親のパスを抽出します。 
432         * 
433         * @param key キー
434         * @return 親のパス
435         */
436        protected String drawParent(String key) {
437                int k = key.lastIndexOf("/");
438
439                String rtn = "";
440                if (k > 0) {
441                        rtn = key.substring(0, key.lastIndexOf("/"));
442                }
443                if ("/".equals(File.separator)) {
444                        rtn = File.separator + rtn;
445                }
446
447                return rtn;
448        }
449
450        /**
451         * 引数のkeyから名称を抽出します。
452         * 
453         * @param key キー(パス)
454         * @return 名称
455         */
456        protected String drawName(String key) {
457                int k = key.lastIndexOf("/");
458
459                String rtn = key;
460                if (k > 0) {
461                        rtn = key.substring(key.lastIndexOf("/") + 1);
462                }
463                return rtn;
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         * @param str 対象文字列
488         * @param chr 指定文字
489         * @return 右側から指定文字を除去後の文字列
490         */
491        protected String rTrim(final String str, char chr) {
492                String rtn = str;
493                int trgPos = 0;
494                for (int i = str.length() - 1; i >= 0; i--) {
495                        if (str.charAt(i) == chr) {
496                                trgPos = i;
497                                // すべて合致した場合は、から文字を返す
498                                if (trgPos == 0) {
499                                        rtn = "";
500                                }
501                        } else {
502                                break;
503                        }
504                }
505
506                if (trgPos > 0) {
507                        rtn = str.substring(0, trgPos);
508                }
509
510                return rtn;
511        }
512
513        /**
514         * ファイル区切り文字を変換します。
515         * 
516         * @param path 変換前文字列
517         * @return 返還後文字列
518         */
519        protected String replaceFileSeparetor(final String path) {
520                if (StringUtil.isNull(path)) {
521                        return "";
522                }
523
524                return path.replaceAll("\\\\", "/");
525        }
526
527        /**
528         * フィルター処理を行う
529         * 
530         * @param list フィルタを行うリスト
531         * @param filter フィルタ情報
532         * @return フィルタ後のリスト
533         */
534        protected FileOperation[] filter(List<FileOperationInfo> list, FileOperationFileFilter filter) {
535                ArrayList<FileOperationInfo> files = new ArrayList<FileOperationInfo>();
536                for (int i = 0; i < list.size(); i++) {
537                        if (filter.accept(list.get(i))) {
538                                files.add(list.get(i));
539                        }
540                }
541                return files.toArray(new FileOperationInfo[files.size()]);
542        }
543
544        /**
545         * InputStreamをbyte[]に変換。
546         * InputStreamのサイズ計算に利用。
547         * 
548         * @param is byte[]変換するInputStream
549         * @return InpusStreamをbyte[]に変換した値
550         */
551        protected byte[] toByteArray(InputStream is) throws IOException {
552                ByteArrayOutputStream output = new ByteArrayOutputStream();
553                try {
554                        byte[] b = new byte[BUFFER_SIZE];
555                        int n = 0;
556                        while ((n = is.read(b)) != -1) {
557                                output.write(b, 0, n);
558                        }
559                        return output.toByteArray();
560                } finally {
561                        output.close();
562                }
563        }
564}
565