S-JIS[2007-10-07/2009-10-23] 変更履歴

FileSetクラス

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の例

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は、ファイルだけでなく プロパティーや文字列等色々なものを想定しているらしい)

Resource(Ant1.5.2以降)のメソッド
メソッド 概要 バージョン
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以降
FileResource(Ant1.7以降)のメソッド
メソッド 概要
File getFile() ファイル(のフルパス)を返す。
File getBaseDir() ベースとなっているディレクトリーを返す。
void touch(long time) ファイルの最終更新日時を変更する。

build.xml以外から使う方法

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インスタンスをセットしておく必要がある。
(セットしておかないと、ファイル一覧取得時に落ちる)


FileSelector

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()を呼び出しているので、それを使っても良さそう)


独自FilenameSelector

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といった処理は面倒なので省略した^^;
	}
}

独自タスクの作り方へ戻る / Ant目次へ戻る / 技術メモへ戻る
メールの送信先:ひしだま