Home > Articles

Global Concepts

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Exploring Ant Data Types

Let's now move on to discuss the data types that Ant provides that aren't exactly tasks. If you are used to data types in programming languages, these are quite a bit different, but they provide services that you'll most certainly need. Specifically, we'll discuss the following:

  • Description type—Describes a project

  • PatternSet—Groups of patterns

  • DirSet—Groups of directories

  • FileSet—Groups of files

  • FileList—Explicit list of files

  • FileMapper—Translates filenames

  • FilterReader—Custom class that filters out files

  • FilterChain—Series of FilterReaders to further filter files

  • FilterSet—Groups of filters

  • Selectors—Provides more control over file selection

  • Class FileSet—A FileSet for class files

  • Path-like structures—A data type resembling a file system path

  • XMLCatalog—Catalog of public XML resources

These elements will be used most often nested inside other tags. Some of them, the PatternSet for instance, are actually implicitly contained within other tags, such as FileSet, so you can use PatternSet tags inside FileSets. Confusing? It won't be in a little while. So, let's dive in and discover what these data types can be used for.

Description Type

The <description> tag is not used often enough. I have a hard time remembering to add it. There is only one place that a <description> tag should go, and that is at the top level of your project, directly under the opening <project> tag. Whatever you place between the open and close <description> tags will be printed on the console whenever Ant is invoked with the -projecthelp switch. Listing 3.20 shows this.

Listing 3.20 The <description> Tag

<project default="deploy" basedir=".">
  <description>You can build me using Ant!</description>
</project>

Don't confuse this tag with the description attribute of the <target> tag. They work together, but if you try to nest a <description> inside a <target>, all the text from the different <description> tags runs together, which is not what you want.

PatternSet

PatternSets are groups of patterns that enable you to easily filter files or directories based on certain patterns. Ant supports three wildcards that you use to create your patterns. They are

  • ? matches a single character

  • * matches zero or more characters

  • ** matches zero or more directories recursively

These metacharacters (with the possible exception of the **) should be familiar to anyone who has ever worked at a file system command line. You can combine these wildcards in interesting ways to specify only those files or directories that you want or don't want. As we go along with PatternSets, you'll see examples of such uses.

As I said before, PatternSets are groups of patterns. They support four nested elements: <include>, <includesfile>, <exclude>, and <excludesfile>. The basic idea is that you nest these tags inside your <patternset> to filter out which files or directories are matched. Listing 3.21 shows how to get all .class files except those with Test in the name. This is recursive, by the way.

Listing 3.21 Filtering .class Files for Test

<patternset id="classfiles">
  <include name="**/*.class"/>
  <exclude name="**/*Test*.class"/>
</patternset>

The id attribute enables you to refer to this PatternSet later by using a refid attribute of some task.

The <includesfile> and <excludesfile> tags both take an src attribute that specifies a file containing patterns to match with. Specifying your patterns in the build file itself seems to be far more common.

All four of these nested elements have if and unless attributes that can be used to keep them from executing if a certain property is not present, or unless a particular property is present.

As you'll see, PatternSets are implicitly contained within several other tags.

It should be noted that each of those four nested tags of PatternSet (and FileSet, and so on) can also be specified as attributes rather than nested elements. Two of them change their names slightly: include becomes includes, and exclude becomes excludes. So, Listing 3.21 could be rewritten as shown in Listing 3.22 to the same effect. (I prefer the nested syntax because I think it is a little easier to read.)

Listing 3.22 PatternSet Using Attributes Instead of Nested Tags

<patternset id="classfiles" includes="**/*.class"
  excludes="**/*Test*.class"/>

DirSet

DirSets give you a way to specify directories by applying a pattern or patterns to a directory structure. Using these patterns, you can easily include certain directories while simultaneously excluding others. DirSets hold an implicit PatternSet, which means the four nested PatternSet elements appear as attributes of the <dirset> tag. You can also nest <patternset> elements, or <include> and <exclude>, and so on. Listing 3.23 shows the use of attributes, whereas Listing 3.24 uses a nested <patternset>. Listing 3.25 uses the PatternSet nested elements to gather those directories that contain the string classes, except for those with the word debug.

Listing 3.23 A Sample DirSet Using Attributes

<dirset dir="${build.dir}" includes="**/classes" excludes="**/*debug*"/>

Listing 3.24 A Sample DirSet Using a Nested PatternSet

<dirset dir="${build.dir}">
  <patternset id="classes">
    <include name="**/classes"/>
    <exclude name="**/*debug*"/>
  </patternset>
</dirset>

Listing 3.25 A Sample DirSet Using PatternSet Elements

<dirset dir="${build.dir}">
  <include name="**/classes"/>
  <exclude name="**/*debug*"/>
</dirset>

It should be noted that the <patternset> in Listing 3.18 has an id attribute. If set, this PatternSet can be referenced in other places in the build file. In our case, a task that works with references could have a refid="classes" attribute to reference the classes PatternSet.

FileSet

Similar to a DirSet is the FileSet, which, as you've probably already guessed, groups files based on a set of patterns. The <fileset> tag takes all the same attributes as <dirset> with one addition: defaultexcludes. This is a Boolean field that says whether to use its default list of files to exclude. This exclude list includes common files that you would want to filter out anyway, such as backup files, files inside CVS directories, and temporary files. The default is to use the default excludes; if you set this to "no", you will not get the benefit of the defaults.

Listings 3.26, 3.27, and 3.28 will look remarkably like the previous three listings because the syntax is almost identical. Like a DirSet, a FileSet contains an implicit PatternSet and works the same way. In these examples, we'll select those files that end in .java, and exclude any files with Test in their names.

Listing 3.26 A Sample FileSet Using Attributes

<fileset dir="${build.dir}" includes="**/*.java" excludes="**/*Test*"/>

Listing 3.27 A Sample FileSet Using a Nested PatternSet

<fileset dir="${build.dir}">
  <patternset id="sources">
    <include name="**/*.java"/>
    <exclude name="**/*Test*"/>
  </patternset>
</fileset>

Listing 3.28 A Sample FileSet Using PatternSet's Elements

<fileset dir="${build.dir}">
  <include name="**/*.java"/>
  <exclude name="**/*Test*"/>
</fileset>

FileList

Although FileSets enable you to dynamically select files based on patterns, sometimes it is easier to specify the files directly because you know up front exactly which files will be needed. The <filelist> tag enables you do this. This element can have an id attribute, and requires both a dir and a files attribute to specify which files (comma delimited) in which directory you care about. Listing 3.29 is a simple example.

Listing 3.29 A Simple FileList

<filelist id="xmlfiles" dir="${etc.dir}"
  files="poolman.xml, struts-config.xml"/>

Remember that wildcards are not allowed here. The <filelist> tag is for explicitly naming files. If you need to dynamically select files, you should use a FileSet.

FileMapper

The FileMapper transforms source filenames into destination filenames for tasks such as <copy>, <junit>, <move>, and so on. Several different types of mappers are included. Here's the list:

  • identity—The target filename is identical to the source filename

  • flatten—The directory structure is disregarded when the target filename is constructed

  • merge—The target filename is the same for all source files

  • glob—Glob matching is performed on the files

  • regexp—Regular expression matching is performed on the files

  • package—The transform is based on the Java package of the source files

The <mapper> tag has several attributes that can be specified. Exactly one of either type or class must be specified. The type attribute would be one of the mapper types listed in the preceding list. If you write your own mapper class, you would specify the full class name as the class attribute. If you do use the class attribute, you will most likely need to use either classpath or classpathref to give Ant access to the class. classpath takes a property value that works out to a path-like structure, and classpathref takes a reference to a path-like structure. The from and to attributes specify what to transform from and to, respectively. Depending on which mapper type you're using, you may specify neither, either, or both the from and to attributes.

Now, on to the types of mappers. The identity type performs no transformation at all from the source filename to the destination filename. This is the default mapper for <copy>, <move>, and a few other tags. You can change which mapper is used by nesting a <mapper> tag inside a task that uses a mapper.

The flatten mapper is useful if you have a directory structure and want all the files in all the directories to be copied to the same directory but without the directory structure. Listing 3.30 shows a copy operation that discards the directories and copies only the files.

Listing 3.30 Copying a File Without the Directory Structure

<copy todir="bar">
  <mapper type="flatten"/>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

The merge type copies all files to the same filename. This could be useful for writing to a UNIX device such as a tape drive, or for creating a tar file (which is what the Ant documentation uses as its example). Listing 3.31 copies all files to a UNIX tape drive.

Listing 3.31 Copying to a Tape Drive

<copy todir="/dev">
  <mapper type="merge" to="mt0"/>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

The ultimate destination is /dev/mt0, which is a typical name for a tape drive on a UNIX box.

Next is the glob type. This uses the filename-globbing wildcard *, and replaces the * in the from attribute with the * value from the to attribute. Listing 3.32 should make this clearer. It copies and renames all .java files to .java.save.

Listing 3.32 Performing a "Backup" of All .java Files

<copy todir="bar">
  <mapper type="glob" from="*.java" to="*.java.save"/>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

Next is the regular expression mapper, regexp. It works just like the glob type, except that you have the matching power of regular expressions and more replacement abilities. If you put parentheses around interesting bits in the source filename, you can refer to these bits using standard naming, such as \1, \2, \3, and so on. Listing 3.33 demonstrates this using the same example as Listing 3.32.

Listing 3.33 Copying with the regexp Mapper

<copy todir="bar">
  <mapper type="regexp" from="^(.*).java$$" to="\1.java.save"/>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

NOTE

It is important to note that for the regexp mapper to work, you must have access to a regular expression library. You essentially have three options. If you're using JDK 1.4, you have a built-in regexp library. If you're using JDK 1.3 or 1.4, you can use Jakarta Regexp or Jakarta ORO. Whichever one you choose to use, you must ensure that it is in the class path prior to execution. And, if you build from source, the regexp library must be in your class path before you build Ant. Otherwise, the Ant regexp support classes won't be built into Ant.

Finally is the package type. According to the Ant manual, this mapper will replace directory separators in the matched source files with dots. This would enable you to generate names based on the Java package scheme, which would be most useful in an uptodate or junit task. Listing 3.34 shows the example from the manual.

Listing 3.34 Mapping Based on Java Package

<mapper type="package"
  from="*Test.java" to="TEST-*Test.xml"/>

There is no definitive list of Ant tasks that use mappers. But if you think about those tasks that take a list of files and output files with similar names, but perhaps with different extensions, you should be able to figure out which tasks support mappers.

FilterReader and FilterChain

A FilterReader is a subclass of java.io.FilterReader. A FilterReader performs some sort of filtering on the stream of data coming from some source. In our case, the source will be files or directories. You can create your own FilterReaders simply by subclassing java.io.FilterReader.

FilterReaders can be used only when contained within a FilterChain, which is simply a chain, or pipeline, of FilterReaders. The basic duty of a FilterReader is to perform some sort of filtering on a file as it is read. Ant comes with several built-in FilterReaders. They are

  • ClassConstants—Looks for constants defined in .class files and outputs them, one per line, in name-value pairs

  • ExpandProperties—Expands Ant properties, such as ${build.dir}, contained in the input data

  • HeadFilter—Returns only the top n lines from the head of the file

  • TailFilter—The opposite of HeadFilter

  • ReplaceTokens—Replaces tokens contained between delimiters

  • StripJavaComments—Removes all comments from .java files

  • StripLineComments—Removes lines that start with user-defined comment characters

  • StripLineBreaks—Removes all end-of-line characters from the input file

  • LineContains—Copies only those lines containing a specified string

  • LineContainsRegexp—Copies only those lines containing a specified regular expression

  • PrefixLines—Adds a specified string to the beginning of every line

  • TabsToSpaces—Converts tab characters to a given number of spaces

ClassConstants

I'll demonstrate each of the FilterReaders using the <copy> task. We'll begin with the ClassConstants filter. This filter looks for all fields in a .class file that are constants (public static final ...), and outputs those lines as key-value pairs. Listing 3.35 shows an example of this. Note that for this FilterReader to work, you must have the Jakarta BCEL package. Also, if you built Ant from source, you must have had the Jakarta BCEL package in your class path at the time of the build.

Listing 3.35 Pulling the Constants from Binary Files and Storing Them in Text Files

<copy todir="bar">
  <filterchain>
    <classconstants/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.class"/>
  </fileset>
  <mapper type="glob" from="*.class" to="*.txt"/>
</copy>

ExpandProperties

The ExpandProperties filter expands any Ant properties that are contained in the data it is processing with values that it knows about. This could be useful if you've specified a filename in the .java file with part of the path given as a property or you want to indicate which version of Java you built with (using the java.version property). Listing 3.36 shows how to use ExpandProperties.

Listing 3.36 Expanding Ant Properties Using the ExpandProperties FilterReader

<copy todir="bar">
  <filterchain>
    <expandproperties/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

HeadFilter and TailFilter

HeadFilter and TailFilter directly mimic the standard UNIX programs head and tail. head retrieves only the first n lines, whereas tail retrieves only the last n lines. HeadFilter and TailFilter are useful if you want to copy only a sample from a series of files rather than the entire file. Listing 3.37 copies the first five lines of each file, whereas Listing 3.38 copies the last five lines of each file.

Listing 3.37 Copying the First Five Lines

<copy todir="bar">
  <filterchain>
    <headfilter lines="5"/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

Listing 3.38 Copying the Last Five Lines

<copy todir="bar">
  <filterchain>
    <tailfilter lines="5"/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

ReplaceTokens

You know how tools such as CVS and SourceSafe replace tokens like $Id$ and $Log$ with information about the current version and history of a file on checkout? You can get the same effect by using the ReplaceTokens filter when copying, moving, and so on. The way it works is that you specify a token to replace and what the replacement should be. You can also specify what the delimiters will be, using the begintoken and endtoken attributes, but the default is the @ symbol. Listing 3.39 demonstrates how to replace a token of @TODAY@ with a customized date.

Listing 3.39 Demonstrating ReplaceTokens

<copy todir="bar">
  <filterchain>
    <replacetokens>
      <token key="TODAY" value="${MY_DATE_FORMAT}"/>
    </replacetokens>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

StripJavaComments

The StripJavaComments filter removes anything that resembles a comment in your Java source files. I'm not really sure why you would want to do this unless perhaps it was to remove commented-out sections that you didn't want to be seen by a customer, or something like that. StripJavaComments takes no parameters; all you have to do is specify it as in Listing 3.40.

Listing 3.40 Removing Java Comments

<copy todir="bar">
  <filterchain>
    <stripjavacomments/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

StripLineComments

This filter removes all lines that start with a user-specified comment string. This filter would be useful if you wanted to remove the comments from some SQL scripts or shell scripts. You can specify multiple comment strings by providing multiple <comment> nested tags as shown in Listing 3.41. Note that for a line to be stripped, the specified comment string must be the first thing on the line.

Listing 3.41 Removes Lines Starting with the Specified Comment Strings

<copy todir="bar">
  <filterchain>
    <striplinecomments>
      <comment value="--"/>
      <comment value="#"/>
    </striplinecomments>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

StripLineBreaks

This filter removes all end-of-line characters, essentially joining the text into one big line. Unlike StripJavaComments, I can readily think of a good use for this one. If you're building a system in which you will be storing scripts (such as Jython, ECMAScript, or even HTML) in database cells, removing line breaks will save on storage. This filter has one optional parameter, linebreaks, that specifies what the end-of-line character is. The default is \r\n, which should have you covered. Listing 3.42 demonstrates how to remove line breaks using the default values.

Listing 3.42 Stripping Off Line Breaks

<copy todir="bar">
  <filterchain>
    <striplinebreaks/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

LineContains

This filter will copy those only lines that contain a specified string. Just as with the StripLineComments filter, you can specify multiple strings to match against; however, if you include multiple strings, the lines must contain all the strings. Listing 3.43 shows how to copy only those lines that contain foo and bar.

Listing 3.43 Copying Lines Containing foo and bar

<copy todir="bar">
  <filterchain>
    <linecontains>
      <contains value="foo"/>
      <contains value="bar"/>
    </linecontains>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

LineContainsRegexp

This filter will copy those only lines that match a specified regular expression. Listing 3.44 shows how to copy only those lines that contain the word foo followed by the word bar after three spaces.

Listing 3.44 Copying Lines Containing foo and bar with a Regular Expression

<copy todir="bar">
  <filterchain>
    <linecontainsregexp>
      <regexp pattern="foo\s{3}bar"/>
    </linecontainsregexp>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

PrefixLines

This filter will copy all lines, prefixing each line with the given string. This is another filter that doesn't seem all that useful, but Listing 3.45 shows an example anyway.

Listing 3.45 Prefixing Each Line with Hello

<copy todir="bar">
  <filterchain>
    <prefixlines prefix="Hello"/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

TabsToSpaces

Finally we come to the TabsToSpaces filter. As you can guess from its name, this filter replaces any tab it finds with an appropriate number of spaces. The single optional attribute, tablength, sets the number of spaces that each tab should be replaced with, but the default is 8 and that is probably a good value. Listing 3.46 shows how to replace each tab with four spaces.

Listing 3.46 Replacing Each Tab with Four Spaces

<copy todir="bar">
  <filterchain>
    <tabstospaces tablength="4"/>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

Using Multiple FilterReaders Together in a FilterChain

Several of the FilterReaders we've just discussed are useful all by themselves. But the FilterChain enables you to string together multiple FilterReaders at one go. Listing 3.47 will copy all Java source files from the ${src.dir} directory, strip all comments, expand Ant properties, and replace instances of the @TODAY@ token with the current date.

Listing 3.47 Using Multiple FilterReaders at a Time

<copy todir="bar">
  <filterchain>
    <stripjavacomments/>
    <expandproperties/>
    <replacetokens>
      <token key="TODAY" value="${DSTAMP}"/>
    </replacetokens>
  </filterchain>
  <fileset dir="foo">
    <include name="**/*.java"/>
  </fileset>
</copy>

FilterSet

The FilterSet is very similar to the ReplaceTokens FilterReader that was covered in the previous section. Using a FilterSet with a task such as copy, you can replace certain text tokens in each file with specified replacements. Just as with the ReplaceTokens FilterReader, the default token delimiter is @, but this can be changed using the begintoken and endtoken attributes. Specifying tokens to replace can be done either with a series of token tags, or by loading the filters from a file of key-value pairs. Listing 3.48 shows using the copy task with a FilterSet to replace instances of @TODAY@ with the current date, and Listing 3.49 loads the filters from a file.

Listing 3.48 Copies Files Replacing @TODAY@ with the Current Date

<copy todir="${dist.dir}">
  <fileset dir="${src.dir}" includes="**/*.java"/>
  <filterset>
    <filter token="TODAY" value="${DSTAMP}"/>
  </filterset>
</copy>

Listing 3.49 Loads Replacement Tokens from a File

<copy todir="${dist.dir}">
  <fileset dir="${src.dir}" includes="**/*.java"/>
  <filterset>
    <filtersfile file="${filters.file.name}"/>
  </filterset>
</copy>

Selectors

You saw in the discussion of FileSet that the decision of whether to include a file was solely based on a filename. But there are times when that approach is insufficient to creating an appropriate list. Selectors provide a means to select files based on factors other than filename. Although you can write your own selector, a number of them are included with Ant. They are

  • contains—Selects files whose contents contain a specified string

  • date—Selects files based on their modification date

  • depend—Selects file that has been modified later than some other file that depends on it

  • depth—Selects files at a certain directory structure depth

  • filename—Selects file based on filename

  • present—Selects file based on its presence or absence from a given directory

  • size—Selects files based on the size of their contents

contains

The contains selector is much like the standard grep utility. It searches the content of a group of files for a specified string and returns those files that contain it. You could use contains to copy only those Java files that reference a certain class, for instance. Listing 3.50 demonstrates this usage.

Listing 3.50 Copies Java Files Containing a Certain Class Reference

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <contains text="MyClass"/>
  </fileset>
</copy>

There is an optional attribute to the contains selector, called casesensitive, which determines whether differences in uppercase and lowercase letters matters. The default setting is true.

date

The date selector enables you to select files that have been modified before, after, or at the exact date and time specified. The optional attribute when controls the time matching. The possible values are before, after, and equal. The default is to select files before the date given. Listing 3.51 copies all Java files modified before noon of July 21, 2002.

Listing 3.51 Copies Java Files Older Than Noon of July 21, 2002

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <date datetime="07/21/2002 12:00 PM"/>
  </fileset>
</copy>

depend

From its name, you might assume that the depend selector selects files on which some other file or files depend. Not so. It selects files that have equivalent files in a different location that are different. The example from the Ant manual shows a method for selecting those files that have changed between two versions of Ant. (I think this selector would have been better named Diff or something indicating that it deals with file differences, not file dependencies.) Listing 3.52 shows the example from the Ant manual.

Listing 3.52 Selecting Files That Changed Between Versions of Ant

<fileset dir="${ant.1.5}/src/main" includes="**/*.java">
  <depend targetdir="${ant.1.4.1}/src/main"/>
</fileset>

The depend selector has an optional attribute called granularity that takes a number of milliseconds to use as a cushion before deciding that two files are different. The default is 0 milliseconds.

depend also can use an optional mapper to define the mapping of source to target files. See the section about FileMappers earlier in this chapter for more details.

depth

The depth selector selects a file based on how deep in the directory structure it is. I don't really see this selector as being very useful, but Listing 3.53 shows an example nonetheless. This listing copies those files from ${src.dir} that are two levels deep or deeper.

Listing 3.53 Selecting Files Based on Tree Depth

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <depth min="2"/>
  </fileset>
</copy>

depth also has a max attribute that sets a limit on how deep the copy will go.

filename

It might seem odd to have a selector that selects files based on filename because that is how FileSets generally select files. However, if you combine the filename selector with other selectors inside selector containers (covered shortly), you can get very fine control over selection. You'll see an example of this shortly, but Listing 3.54 shows the basic usage of filename.

Listing 3.54 Selecting Files Based on Filename

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*">
    <filename name="**/*.java"/>
  </fileset>
</copy>

present

The present selector selects files that are either in both the source and target directories or only in the source directory. This selector could be used to select only those files that exist in version 2 of your project but not in version 1. Listing 3.55 shows this.

Listing 3.55 Selecting Files Based on Presence

<copy todir="${tmp.dir}">
  <fileset dir="${v1.src.dir}" includes="**/*.java">
    <present present="srconly" targetdir="${v2.src.dir}"/>
  </fileset>
</copy>

The other possible value for the present attribute is both, which would select only those files that appear in both the source and target directories.

size

The size selector enables you to select files based on their size. Three attributes handle the file selection. The first attribute is value, which is the size you want to test against. The second attribute, units, determines what the size you specified in the value attribute represents. The default is bytes. Possible values for units include Ki, Mi, and Gi for power-of-2 kilobytes, megabytes, and gigabytes, respectively. Finally, the when attribute specifies to select whether files are less, more, or equal to the value attribute. The default is equal. Listing 3.56 copies only those files that are smaller than 1KB.

Listing 3.56 Selecting Files Smaller Than One Kilobyte

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <size value="1" units="Ki" when="less"/>
  </fileset>
</copy>

There are also a number of container selectors. Such selectors can contain other selectors, including other containers, to make them work together. The included container selectors are

  • and—Selects a file if all contained selectors select it

  • or—Selects a file if any contained selector selects it

  • not—Reverses its single contained selector

  • majority—Selects a file if a majority of contained selectors select it

  • none—Selects a file if none of the contained selectors select it

  • selector—Useful for defining a reusable selector

and

The and selector will select a file only if all of its contained selectors select it. You would use this container if you wanted to apply several conditions to a file before selecting it. For example, if you wanted to combine selectors to select all those Java source files that contained the word Test in their name, were larger than 1KB, and were older than July 21, 2002, you could write something like Listing 3.57.

Listing 3.57 Selecting Files Based on Multiple Selectors

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <and>
      <filename name="**/*Test*.java"/>
      <date datetime="07/21/2002 12:00 PM" when="before"/>
      <size value="1" units="Ki" when="more"/>
    </and>
  </fileset>
</copy>

or

The or selector is similar to the and selector except that it will select a file if any of its contained selectors selects it. If we change Listing 3.54 to use an or selector instead of an and selector, we'll get all Java files that contain Test in the name, or were modified before July 21, 2002, or are greater than 1KB in size. Listing 3.58 shows this.

Listing 3.58 Selecting Files Based One of Several Selectors

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <or>
      <filename name="**/*Test*.java"/>
      <date datetime="07/21/2002 12:00 PM" when="before"/>
      <size value="1" units="Ki" when="more"/>
    </or>
  </fileset>
</copy>

not 

The not selector can contain only one selector and it will reverse the condition. The single selector can be another container, so if we wanted to take Listing 3.57 and get only those Java files that do not contain the word Test in their name, are smaller than 1KB, and are not older than July 21, 2002, Listing 3.59 would work.

Listing 3.59 Negating a Contained Selector Using the not Selector

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <not>
      <and>
        <filename name="**/*Test*.java"/>
        <date datetime="07/21/2002 12:00 PM" when="before"/>
        <size value="1" units="Ki" when="more"/>
      </and>
    </not>
  </fileset>
</copy>

majority

The majority selector is sort of a combination of the and and or selectors. It will select a file if a simple majority of contained selectors select a file. Turning once again to Listing 3.54, we can modify it so that files will be selected that meet two of the three criteria for selection by changing the and selector to the majority selector as shown in Listing 3.60.

Listing 3.60 Selecting Files Based on a Majority of Multiple Selectors

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <majority>
      <filename name="**/*Test*.java"/>
      <date datetime="07/21/2002 12:00 PM" when="before"/>
      <size value="1" units="Ki" when="more"/>
    </majority>
  </fileset>
</copy>

none

The none selector will select only those files that were selected by none of the contained selectors. You could get the same effect by wrapping an and or or container inside a not. Listing 3.61 produces results identical to those produced by Listing 3.59.

Listing 3.61 Negating a Contained Selector Using none

<copy todir="${tmp.dir}">
  <fileset dir="${src.dir}" includes="**/*.java">
    <none>
      <filename name="**/*Test*.java"/>
      <date datetime="07/21/2002 12:00 PM" when="before"/>
      <size value="1" units="Ki" when="more"/>
    </none>
  </fileset>
</copy>

selector

The final selector, selector, is most useful outside of a task, with an ID assigned to it with the id attribute. It can be used later by referring to it with the refid attribute. Listing 3.62 demonstrates this by creating a selector in the init target, and then using it in the copy target.

Listing 3.62 Creating a Selector for Later Use

<target name="init">
  <selector id="copySelector">
    <and>
      <filename name="**/*Test*.java"/>
      <date datetime="07/21/2002 12:00 PM" when="before"/>
      <size value="1" units="Ki" when="more"/>
    </and>
  </selector>
</target>

<target name="copy" depends="init">
  <copy todir="${tmp.dir}">
    <fileset dir="${src.dir}" includes="**/*.java">
      <selector refid="copySelector"/>
    </fileset>
  </copy>
</target>

Class FileSet

There is a variation of the FileSet in the optional package called a Class FileSet. It is used in a similar fashion to FileSet, but it operates on classes. It provides the same attributes as a regular FileSet, but it has an additional one. Class FileSet works off the notion of a root file. After you've specified a root file, it selects all files on which the root class depends. You can specify multiple root files and you can specify a Root FileSet to get the dependencies for several files at one time. Listing 3.63 demonstrates copying all .class files that the class myproject.test.TestClass0 depends on.

Listing 3.63 Copying the Dependencies of a Class

<classfileset id="testClasses" dir="${build.dir.classes}"
  rootclass="myproject.test.TestClass0"/>

<target name="copy" depends="init">
  <copy todir="${test.dir}">
    <fileset refid="testClasses"/>
  </copy>
</target>

Notice that we defined a classfileset outside of a target. Class FileSets can be defined both in and out of a target. They can't be used inside another task, however. This seems odd because FileSets can be used inside another task. The way around this is to define your Class FileSet, give it an ID, and then reference this ID with a fileset tag as in Listing 3.63.

If you want to specify more than one root file, you can use the nested root tags. Listing 3.64 will copy all dependencies of the two classes listed in the root tags.

Listing 3.64 Copying the Dependencies of Two Classes

<classfileset id="testClasses" dir="${build.dir.classes}">
  <root classname="myproject.test.TestClass0"/>
</classfileset>

<target name="copy" depends="init">
  <copy todir="${test.dir}">
    <fileset refid="testClasses"/>
  </copy>
</target>

Finally, if you have several classes that you need to generate dependencies for, the nested rootfileset tag is the ticket. It looks just like a fileset tag and using it will result in the classes matching it to be used by the containing Class FileSet to generate dependencies. Listing 3.65 demonstrates selecting files that are depended on by all the test classes.

Listing 3.65 Copying the Dependencies of Several Classes

<classfileset id="testClasses" dir="${build.dir.classes}">
  <rootfileset dir="${build.dir.classes}" includes="**/Test*.class"/>
</classfileset>

<target name="copy" depends="init">
  <copy todir="${test.dir}">
    <fileset refid="testClasses"/>  
  </copy>
</target>

Path-Like Structures

It sounds like an odd thing, but it is extremely useful and you'll be using them a lot. A path-like structure is essentially a platform-aware path or classpath. You can create these structures much more easily than setting up a system classpath, and then pass them to tasks that need them. You can create paths by specifying directories or JARs directly, but you can also nest FileSets or DirSets inside them to quickly add several jars or directories. Listing 3.66 shows a quick example of creating a classpath. Notice that we specify two JAR files directly, and then we add all the JARs in the ${lib.dir} using a FileSet.

Listing 3.66 Setting Up a Classpath

<path id="classpath">
  <pathelement path="${xalan.dir}/xalan.jar"/>
  <pathelement path="${castor.jar}"/>
  <fileset dir="${lib.dir}">
    <include name="**/*.jar"/>
  </fileset>
</path>

This block can live inside a target, such as init, or it can live outside of any target. I prefer to have it live inside init rather than floating around by itself. You can still access it from any target, which is what you want. You can reference this path inside another path using its refid, and you can pass it to javac and other tasks in the same manner. Listing 3.67 uses the path defined in Listing 3.66 as the classpath for javac.

Listing 3.67 Using a Classpath

<javac srcdir="${src.dir}" destdir="${build.dir.classes}">
  <classpath refid="classpath"/>
</javac>

You can also reference one path from within another and add more to it. For example, you may define a classpath in your init target that has the common classes that several tasks will need, but when you compile, you need to add another JAR file. This is easily accomplished and Listing 3.68 demonstrates this. It creates the initial classpath in init, and then adds to it at compile time.

Listing 3.68 Using a Classpath

<target name="init">
  <path id="classpath">
    <pathelement path="${xalan.dir}/xalan.jar"/>
    <pathelement path="${castor.jar}"/>
    <fileset dir="${lib.dir}">
      <include name="**/*.jar"/>
    </fileset>
  </path>
</target>

<target name="compile" depends="init">
  <javac srcdir="${src.dir}" destdir="${build.dir.classes}">
    <classpath>
      <path refid="classpath"/>
      <pathelement path="${oro.jar}"/>
    </classpath>
  </javac>
</target>

You can see from this simple example that path-like structures are quite powerful. They are certainly easier and less error-prone than trying to set your classpath either at a system level or in the Ant startup script.

XMLCatalog

The final data type is the XMLCatalog. It is used to create a repository of XML DTDs or entities for quick resolution without having to go to the Internet. This enables you to store DTDs locally, even though the public ID says they live elsewhere. This saves time when processing an XML document that needs the DTD or elements. The only tasks that currently work with an XMLCatalog are the xslt core task and the optional xmlvalidate. To create an XMLCatalog with a DTD in it, you must first copy the DTD to a local directory. Then you create the XMLCatalog in your init target and reference it later. Listing 3.69 creates an XMLCatalog with the DTD for the Enterprise JavaBeans deployment descriptor. It then references this catalog in an xmlvalidate task.

Listing 3.69 Creates and Uses an XMLCatalog

<target name="init">
  <xmlcatalog id="ejbDTDs">
    <dtd 
    publicId="-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN"
    location="c:/DTDs/ejb-jar-2.0.dtd"/>
    <dtd
    publicId="-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN"
    location="ejb-jar-1.1.dtd"/>
  </xmlcatalog>
</target>

<target name="validate" depends="init">
  <xmlvalidate file="${etc.dir}/ejb-jar.xml">
    <xmlcatalog refid="ejbDTDs"/>
  </xmlvalidate>
</target>

Notice that the listing specifies two DTDs. The first uses a location with an absolute path, whereas the second uses a relative path. This relative path is relative to the basedir that you set in your project tag.

  • + Share This
  • 🔖 Save To Your Account