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