Javaでファイル、ディレクトリを操作する場合、java.io.Fileやjava.nio.file.Pathを用いて表現します。
EclipseではIPathないし、IResourceを用いて表現するのですが、これもうほんと、使いにくい。
といっても、Eclipseでファイルを扱おうと思ったら避けることは出来ませんので、理解して付き合っていくしかありません。
IPath
IPathはJavaのAPIで言えば、java.nio.file.Pathとほぼ同等の機能です。ファイルやディレクトリなどの場所(パス)を表現します。
Eclipseプラグイン開発において、IPathは主に以下のパスを表現します。
- フルパス:ワークスペースをルートとする、ワークスペース空間における絶対パス表現
- ロケーション(ローカルシステム)パス:OS固有の絶対パス表現
- 相対パス:絶対パスではないパス表現
フルパスというのは、ワークスペースをルートとする、Eclipseシステム系の絶対パスを示します。
たとえば、右図の様なワークスペースのプロジェクト構造をしているとき、AプロジェクトのA.javaは「/A/src/A.java」と表現され、BプロジェクトのdlinkフォルダのTest.classは「/B/dlink/Test.class」と表現されます。
ルートであるワークスペースのパスは「/」です。この辺はUnixと同じ表現ですね。@でも何でも良いから別な文字にしときゃ良かったのに。
一方で、ロケーションパスというのはOSのファイルシステムにおける絶対パスを表します。
実際にフルパス、ロケーションパスを見たければ、プログラムを書かなくても、プロパティビューから確認することが出来ます。
右図のワークスペースは、「F:\workspace」に配置しているのですが、プロジェクトAとプロジェクトBのローカルパスを表示すると下図の様に「F:\workspace」とは全然関係ないところに配置されていることが分かります。
従って「ワークスペースのディレクトリ(F:\workspace)」+「フルパス」としてもOS上のファイルパスを取得することが出来るとは限りません。
なお、ロケーションパスには、生ロケーションパス(Raw Location Path)が存在しますが、これは使うことないと思います。Eclipseのフォルダリンクは、リンク先を絶対パスで指定するタイプのものと、変数で指定する仮想リンクがあるのですが、生ロケーションパスが表現するのは後者のほうです。例えば、Test.javaはこんな風に出てきます。「PARENT-1-PROJECT_LOC/java/Test.java」。普通にロケーションパスを取得すれば、変数の所は解決してくれるので、考える必要はありません。
相対パスは、Bundle内のリソースパスだったり、特定のパスからの相対パスだったり等、使う場所や条件によって意味が変わる通常の相対パスです。
IResource
IResourceはワークスペース空間におけるリソース(ワークスペース、プロジェクト、フォルダ、ファイル)を表現するインターフェースです。ワークスペース空間におけるリソースというのは、要するにフルパスで表現できるリソースを表します。
JavaのAPIでいうと、パス以外の付加情報や操作機能を持っているという点でjava.io.Fileがよく似ています。
java.io.Fileと違うのは、ルート(ワークスペース)、プロジェクト、フォルダ、ファイルと4種類のインターフェースに派生する点と、IFile(ファイルを表現するIResource)から内容を編集したときに、Eclipseのローカル履歴に変更情報を残すことが出来る点でしょう。
IResource | 説明 | 取得方法 |
---|---|---|
IWorkspaceRoot | ワークスペースを表す | IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
IProject | プロジェクトを表す | IProject project = root.getProject("B"); |
IFolder | フォルダーを表す | container.getFolder(new Path("hoge")); |
IFile | ファイルを表す | container.getFile(new Path("hoge.txt")); |
containerはIWorkspaceRootか、IProject、IFolderのことです。IContainerのメソッドの引数として渡すIPathは、containerからの相対パスです。
getProject,getFolder,getFileによるプロジェクト、フォルダー、ファイルの取得は、存在しないリソースも表します。この辺はもうAPI設計のミスだと思うわー。newProject,createFolderとかにして、存在する場合はエラーにすべきだと思うわー。
IProject、IFolder、IFileに関わらず、存在する場合のみIResourceを取得したい場合は、container.findMemberメソッドを使います。
IPathからjava.io.Fileやjava.nio.Pathへの変換
Eclipse内部だけで完結するならIPath,IResourceで全て処理すれば良いのだが、大概そうもいかない。普通のライブラリって、ファイル表現にFileだったりPathを使うわけで、IPathやIResourceのままだと困るのだ。
結論を言うと、IPathやIResourceから確実にFileやPathに変換する方法はない。
基本は「IPath→IResource→FileやPath」という変換手順を辿らなければならないが、「IPath→IResource」の段階で絶対の手順というものはないし、「IResource→FileやPath」も必ず成功するとは限らない。
IPath→IResource
まずは渡されたIPathがフルパスなのか、ロケーションパスなのか、相対パスなのか考えなくてはならない。相対パスの場合は、何に対して相対なのかで話が変わるから、ここでは考えない。
フルパスなのか、ロケーションパスなのかについては、Windowsの場合は楽です。「~:\」ないし「\\」から始まっていたらロケーションパスです。そのままFileやPathに変換しましょう。しかし、これがUnix系の場合だったら…どうすんだろうね。
で、フルパスからIResourceに変換する一番簡単な方法は以下の方法だと思う。
IResource resource = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path("パス"));
なお、パスが示す先がFolderであっても、ファイルとしてアクセスするような処理をしない限り、実はgetFileで特に問題ない。
findMemberだと、存在しないファイルのパスを解決できないので、getFileを使うと良い。
IResource→File,Path
単純には以下のようにすれば良い。しかし、プロジェクトを閉じている場合や、リンクを解決できない場合などで、getLocationはnullが返ることがあるので、要注意
File file = resource.getLocation().toFile(); Path path = Paths.get(resource.getLocation().toOSString());