Ant自作タスクでfileset要素を扱うクラス。
独自タスク.java:
import java.io.File; import java.util.*; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.FileSet;
public class 独自タスク extends Task { /** build.xmlで指定されたFileSetの一覧 */ protected List fss = new ArrayList(); public void addFileset(FileSet set) { fss.add(set); }
/** 独自タスクの実行 */
public void execute() throws BuildException {
for (int i = 0; i < fss.size(); i++) {
FileSet fs = (FileSet) fss.get(i);
DirectoryScanner ds = fs.getDirectoryScanner(getProject());
String[] names = ds.getIncludedFiles();
for (int j = 0; j < names.length; j++) {
File f = new File(ds.getBasedir(), names[j]);
execute(f); //個別ファイルの処理
}
}
}
/** * ファイル処理 * @param f ファイル * @throws BuildException */ protected void execute(File f) throws BuildException { log(f.getAbsolutePath(), Project.MSG_INFO); 〜 } }
FileSet#getDirectoryScanner()を呼び出すと、filesetの内容に従ったファイルの探索(ファイル一覧の作成)が行われる。
その後DirectoryScanner#getIncludedFiles()を呼び出すことにより
ファイル名の一覧が取得できる。
これはbuild.xmlのfilesetタスクで指定された条件を満たすファイルのみの一覧となる。
すなわち、excludeで指定されたファイルは除外され、includeの「**」によるディレクトリ内の探索分も含まれる。
このファイル一覧の「ファイル名」は、filesetタスクのdirからの相対パスになる。
したがってDirectoryScanner#getBaseDir()やFileSet#getDir()等を使って、基準となるディレクトリーと合体させる必要がある。
なお、他にもgetIncludedDirectories()やgetNotIncludedFiles()・getExcludedFiles()といったメソッドがある。not-includeとexcludeの違いって一体…^^;
参考: @ITのAntで使う新規タスクにネストした要素を渡す
Ant1.7から、ファイル一覧系データタイプはリソースとして統合された。
すなわちFileSet(やFileList、DirSet)クラスはResourceCollectionインターフェースを実装するようになった。
これにより、一覧取得の方法が変わった。
独自タスク.java(上記の例の改):
mport java.io.File; import java.util.*; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; import org.apache.tools.ant.Task; import org.apache.tools.ant.types.Resource; import org.apache.tools.ant.types.ResourceCollection; import org.apache.tools.ant.types.resources.FileResource;
public class 独自タスク extends Task { /** build.xmlで指定されたFileSet等の一覧 */ protected List<ResourceCollection> rclist = new ArrayList<ResourceCollection>(); public void add(ResourceCollection rc) { rclist.add(rc); }
/** 独自タスクの実行 */
@Override
public void execute() throws BuildException {
for (ResourceCollection rc : rclist) {
for (Iterator<?> i = rc.iterator(); i.hasNext();) {
Resource r = (Resource) i.next();
if (r instanceof FileResource) {
FileResource fr = (FileResource) r;
File f = fr.getFile();
// f = new File(fr.getBaseDir(), fr.getName());
execute(f); //個別ファイルの処理
}
}
}
}
〜 }
FileSetクラスを直接扱う代わりに、ResourceCollectionインターフェースを使う。
ResourceCollectionにはiterator()というメソッドがあり、これでリソース(この場合はファイル)を1個ずつ取得できる。
(なお、FileSetクラスの場合、iterator()内の実装はAnt1.6以前のDirectoryScannerを使う方法と同じ)
自分はJDK1.5スタイルでジェネリクス(List<ResourceCollection>)やアノテーション(@Override)を使ってコーディングしているが、AntのライブラリーはJDK1.2(クラスバージョン46.0)でコンパイルされているので、ジェネリクス
には当然対応していない。
したがってiterator()の戻り型はIterator<?>にしか出来ない。(明示的に型を指定してキャストしても、警告になってしまう)
そのIteratorのnext()はResourceクラスを返す。が、
Antのライブラリーはジェネリクスに対応していないので、Object型をResourceにキャストする必要がある。
上記の例ではその後instanceofでキャストし直しているので、本当は変数rはObject型でいい。(上記は例なのでわざわざResourceにしてみた)
ResourceにはgetName()というメソッドがあるのでファイル名は取れるのだが、filesetタグで指定されていたdir(ディレクトリー名)はそこには含まれない!
(要するにフルパス(絶対パス)ではない)
フルパスで取得する為のgetFile()メソッドは(あるいはdirを取得する為のgetBaseDir()も)FileResourceにしか無いので、FileResourceにキャストする必要がある。
(Resourceは、ファイルだけでなく プロパティーや文字列等色々なものを想定しているらしい)
メソッド | 概要 | バージョン | |
---|---|---|---|
String | getName() | リソースの名前(ファイルリソースの場合はファイル名やディレクトリー名)を返す。 | |
boolean | isExists() | 実際に存在しているファイルならtrueを返す。 | |
boolean | isDirectory() | ディレクトリーの場合、trueを返す。 | |
long | getSize() | リソースのサイズ(ファイルリソースの場合はファイルサイズ)を返す。 | Ant1.6.3以降 |
long | getLastModified() | ファイルの最終更新日時を返す。(不明な場合は0を返す) | |
InputStream | getInputStream() | リソースの内容をInputStreamで返す。 | Ant1.7以降 |
OutputStream | getOutputStream() | リソースへ出力できるOutputStreamを返す。 | Ant1.7以降 |
boolean | isFilesystemOnly() | ファイルとして扱える場合、trueを返す。 ぱっと探したところでは、FileResourceくらいしかtrueを返さないようだ。 |
Ant1.7以降 |
メソッド | 概要 | |
---|---|---|
File | getFile() | ファイル(のフルパス)を返す。 |
File | getBaseDir() | ベースとなっているディレクトリーを返す。 |
void | touch(long time) | ファイルの最終更新日時を変更する。 |
FileSetクラスを使って、build.xmlを定義せずに、Javaプログラム内からファイル一覧を取得することが出来る。[2009-10-03]
import java.io.File; import java.util.Iterator; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.PatternSet.NameEntry; import org.apache.tools.ant.types.resources.FileResource;
//●FileSetの初期化● FileSet fset = new FileSet(); fset.setProject(new Project()); fset.setDir("C:/temp"); //includeの例 NameEntry include1 = fset.createInclude(); include1.setName("**/*.txt"); NameEntry include2 = fset.createInclude(); include2.setName("**/*.log"); //excludeの例 NameEntry exclude = fset.createExclude(); exclude.setName("**/dummy*"); //●ファイル一覧取得の実行● for (@SuppressWarnings("unchecked") Iterator<FileResource> i = fset.iterator(); i.hasNext();) { FileResource fr = i.next(); File f = fr.getFile(); // f = new File(fr.getBaseDir(), fr.getName()); System.out.println(f); //個別ファイルの処理 }
FileSetインスタンスに対して、Projectインスタンスをセットしておく必要がある。
(セットしておかないと、ファイル一覧取得時に落ちる)
Ant1.5から、セレクターというものが導入されたらしい。[2009-10-23]
これは、ファイルの条件をincludeやexcludeより細かく指定する為のものと思われる。
セレクター | 説明 | |
---|---|---|
FileSelector | インターフェース | セレクターの一番親となるインターフェース。 |
FilenameSelector | クラス | ファイル名(baseDirを除いた部分)でパターンマッチングを行う。 includeに相当。 negatedにtrueをセットしておくとexclude相当になる。 |
AndSelector | クラス | 複数のFileSelectorを追加できる。全ての条件を満たしたとき真となる。 |
OrSelector | クラス | 複数のFileSelectorを追加できる。1つでも条件を満たしたとき真となる。 |
NoneSelector | クラス | 複数のFileSelectorを追加できる。1つでも条件を満たすと偽になる。 |
NotSelector | クラス | 1つのFileSelectorを追加できる。それが条件を満たすと偽になる。 |
exclude相当の事を行いたい場合は、NoneSelector又はNotSelectorを組み合わせることでも実現できる。
import org.apache.tools.ant.types.selectors.*;
Project project = new Project(); FileSet fileSet = new FileSet(); fileSet.setProject(project); fileSet.setDir("C:/temp"); //include相当 FilenameSelector include1 = new FilenameSelector(); include1.setName("**/*.txt"); FilenameSelector include2 = new FilenameSelector(); include2.setName("**/*.log"); OrSelector or = new OrSelector(); or.addFilename(include1); or.addFilename(include2); fileSet.addOr(or); //exclude相当 FilenameSelector exclude = new FilenameSelector(); exclude.setName("**/dummy*"); NoneSelector none = new NoneSelector(); none.addFilename(exclude); fileSet.addNone(none);
各SelectorクラスにはaddProject()というメソッドがあるが、とりあえずセットしなくても動くようだ。
また、それぞれのセレクターを追加する為に、addFilename()だのaddOr()だの、追加したいクラス名毎のメソッドを呼んでいる。
Ant1.6からは、add()メソッドを呼び出せば どのセレクターでも追加できる。
(Ant1.5でも内部では皆appendSelector()を呼び出しているので、それを使っても良さそう)
Antで用意されているFilenameSelectorは、ファイル名部分(baseDirを除いた部分)でパターンマッチングを行う。[2009-10-23]
パターンマッチングを行うメソッドは、以下のようになっている。
public boolean isSelected(File basedir, String filename, File file) { validate(); return (SelectorUtils.matchPath(pattern, filename, casesensitive) == !(negated)); }
patternは、setName()でセットしたパターン(例:"**/*.txt")だと思ってよい。
(末尾に自動的に「/**」が付けられたりすることもあるが)
で、引数のbasedir(例:C:\temp)とfilename(例:zzz\aaa.txt)を合わせたものがfile(C:\temp\zzz\aaa.txt)。
しかし見て分かる通り、filenameしか判定に使っていない。
これだと、パターンを指定するときにFileSetのdir部分を除外しておかないといけないので、面倒な時がある。
そこで、fileを使用したクラスを作ってみた。
…単純にFilenameSelectorを拡張してisSelected()だけオーバーライドすれば良さそうに思えるが、Antの拡張性の無さがここでも遺憾なく発揮され、patternやcasesensitive・negatedといったフィールドはprivateでゲッターメソッドも無いので、結局ソースをコピペする羽目になった(嘆)
import java.io.File; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.types.selectors.FilenameSelector; import org.apache.tools.ant.types.selectors.SelectorUtils;
public class FileSelector extends FilenameSelector { protected String pattern; public void setName(String dir, String filename) { String pattern; if (dir.endsWith("/") || dir.endsWith("\\")) { pattern = dir + filename; } else { pattern = dir + "/" + filename; } pattern = pattern.replace('/', File.separatorChar).replace('\\', File.separatorChar); this.pattern = pattern; } @Override public void verifySettings() { if (pattern == null) { setError("The name attribute is required"); } } @Override public boolean isSelected(File basedir, String filename, File file) throws BuildException { validate(); return SelectorUtils.matchPath(pattern, file.getAbsolutePath(), true); //casesensitiveやnegatedといった処理は面倒なので省略した^^; } }