summaryrefslogtreecommitdiff
path: root/manual/Types/custom-programming.html
blob: 6015d416690f2b14b95e26f0b985295b55e0cea3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
<!DOCTYPE html>
<!--
   Licensed to the Apache Software Foundation (ASF) under one or more
   contributor license agreements.  See the NOTICE file distributed with
   this work for additional information regarding copyright ownership.
   The ASF licenses this file to You under the Apache License, Version 2.0
   (the "License"); you may not use this file except in compliance with
   the License.  You may obtain a copy of the License at

       https://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
-->
<html lang="en">
  <head>
    <link rel="stylesheet" type="text/css" href="../stylesheets/style.css">
    <title>Custom Components</title>
  </head>
  <body>
    <h2>Custom Components</h2>
    <h3>Overview</h3>
    <p>
      Custom components are conditions, selectors, filters and other objects that are defined
      outside Apache Ant core.
    </p>
    <p>
      In Ant 1.6 custom conditions, selectors and filters has been overhauled.
    </p>
    <p>
      It is now possible to define custom conditions, selectors and filters that behave like Ant
      Core components.  This is achieved by allowing datatypes defined in build scripts to be used
      as custom components if the class of the datatype is compatible, or has been adapted by an
      adapter class.
    </p>
    <p>
      The old methods of defining custom components are still supported.
    </p>
    <h3>Definition and Use</h3>
    <p>
      A custom component is a normal Java class that implements a particular interface or extends a
      particular class, or has been adapted to the interface or class.
    </p>
    <p>
      It is exactly like writing a <a href="../develop.html#writingowntask">custom task</a>.  One
      defines attributes and nested elements by writing <em>setter</em> methods and <em>add</em>
      methods.
    </p>
    <p>
      After the class has been written, it is added to the ant system by
      using <code>&lt;typedef&gt;</code>.
    </p>
    <h3 id="customconditions">Custom Conditions</h3>
    <p>
      Custom conditions are datatypes that
      implement <code class="code">org.apache.tools.ant.taskdefs.condition.Condition</code>.  For
      example a custom condition that returns true if a string is all upper case could be written
      as:
    </p>
    <pre>
package com.mydomain;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.condition.Condition;

public class AllUpperCaseCondition implements Condition {
    private String value;

    // The setter for the "value" attribute
    public void setValue(String value) {
        this.value = value;
    }

    // This method evaluates the condition
    public boolean eval() {
        if (value == null) {
            throw new BuildException("value attribute is not set");
        }
        return value.toUpperCase().equals(value);
    }
}</pre>
    <p>
      Adding the condition to the system is achieved as follows:
    </p>
    <pre>
&lt;typedef
    name="alluppercase"
    classname="com.mydomain.AllUpperCaseCondition"
    classpath="${mydomain.classes}"/&gt;</pre>
    <p>
      This condition can now be used wherever a Core Ant condition is used.
    </p>
    <pre>
&lt;condition property="allupper"&gt;
    &lt;alluppercase value="THIS IS ALL UPPER CASE"/&gt;
&lt;/condition&gt;</pre>
    <h3 id="customselectors">Custom Selectors</h3>
    <p>
      Custom selectors are datatypes that
      implement <code class="code">org.apache.tools.ant.types.selectors.FileSelector</code>.
    </p>
    <p>
      There is only one method required, <code class="code">public boolean isSelected(File basedir,
      String filename, File file)</code>.  It returns true or false depending on whether the given
      file should be selected or not.
    </p>
    <p>
      An example of a custom selection that selects filenames ending
      in <samp>.java</samp> would be:
    </p>
    <pre>
package com.mydomain;
import java.io.File;
import org.apache.tools.ant.types.selectors.FileSelector;
public class JavaSelector implements FileSelector {
    public boolean isSelected(File b, String filename, File f) {
        return filename.toLowerCase().endsWith(".java");
    }
}</pre>
    <p>
      Adding the selector to the system is achieved as follows:
    </p>
    <pre>
&lt;typedef
    name="javaselector"
    classname="com.mydomain.JavaSelector"
    classpath="${mydomain.classes}"/&gt;</pre>
    <p>
      This selector can now be used wherever a Core Ant selector is used, for example:
    </p>
    <pre>
&lt;copy todir="to"&gt;
    &lt;fileset dir="src"&gt;
        &lt;javaselector/&gt;
    &lt;/fileset&gt;
&lt;/copy&gt;</pre>
    <p>
      One may use <code class="code">org.apache.tools.ant.types.selectors.BaseSelector</code>, a
      convenience class that provides reasonable default behaviour.  It has some predefined
      behaviours you can take advantage of. Any time you encounter a problem when setting attributes
      or adding tags, you can call <code class="code">setError(String errmsg)</code> and the class
      will know that there is a problem. Then, at the top of
      your <code class="code">isSelected()</code> method call <code class="code">validate()</code>
      and a <code>BuildException</code> will be thrown with the contents of your error
      message. The <code class="code">validate()</code> method also gives you a last chance to check
      your settings for consistency because it
      calls <code class="code">verifySettings()</code>. Override this method and
      call <code class="code">setError()</code> within it if you detect any problems in how your
      selector is set up.
    </p>
    <p>
      To write custom selector containers one should
      extend <code class="code">org.apache.tools.ant.types.selectors.BaseSelectorContainer</code>.
      Implement the <code class="code">public boolean isSelected(File baseDir, String filename, File
      file)</code> method to do the right thing. Chances are you'll want to iterate over the
      selectors under you, so use <code class="code">selectorElements()</code> to get an iterator
      that will do that.
    </p>
    <p>
      For example to create a selector container that will select files if a certain number of
      contained selectors select, one could write a selector as follows:
    </p>
    <pre>
public class MatchNumberSelectors extends BaseSelectorContainer {
    private int number = -1;
    public void setNumber(int number) {
        this.number = number;
    }
    public void verifySettings() {
        if (number &lt; 0) {
           throw new BuildException("Number attribute should be set");
        }
    }
    public boolean isSelected(File baseDir, String filename, File file) {
        validate();
        int numberSelected = 0;
        for (Enumeration e = selectorElements(); e.hasNextElement();) {
            FileSelector s = (FileSelector) e.nextElement();
            if (s.isSelected(baseDir, filename, file)) {
                numberSelected++;
            }
        }
        return numberSelected == number;
    }
}</pre>
    <p>
      To define and use this selector one could do:
    </p>
    <pre>
&lt;typedef name="numberselected"
         classname="com.mydomain.MatchNumberSelectors"/&gt;
...
&lt;fileset dir="${src.path}"&gt;
    &lt;numberselected number="2"&gt;
        &lt;contains text="script" casesensitive="no"/&gt;
        &lt;size value="4" units="Ki" when="more"/&gt;
        &lt;javaselector/&gt;
    &lt;/numberselected&gt;
&lt;/fileset&gt;</pre>
    <p>
      <em>The custom selector</em>
    </p>
    <p>
      The custom selector was the pre Ant 1.6 way of defining custom selectors.  This method is
      still supported for backward compatibility.
    </p>
    <p>
      You can write your own selectors and use them within the selector containers by specifying
      them within the <code>&lt;custom&gt;</code> tag.
    </p>
    <p>
      To create a new Custom Selector, you have to create a class that
      implements <code class="code">org.apache.tools.ant.types.selectors.ExtendFileSelector</code>.
      The easiest way to do that is through the convenience base
      class <code class="code">org.apache.tools.ant.types.selectors.BaseExtendSelector</code>, which
      provides all of the methods for supporting <code>&lt;param&gt;</code> tags. First, override
      the <code class="code">isSelected()</code> method, and optionally
      the <code class="code">verifySettings()</code> method. If your custom selector requires
      parameters to be set, you can also override the <code class="code">setParameters()</code>
      method and interpret the parameters that are passed in any way you like. Several of the core
      selectors demonstrate how to do that because they can also be used as custom selectors.
    </p>
    <p>
      Once that is written, you include it in your build file by using
      the <code>&lt;custom&gt;</code> tag.
    </p>

    <table class="attr">
      <tr>
        <th scope="col">Attribute</th>
        <th scope="col">Description</th>
        <th scope="col">Required</th>
      </tr>
      <tr>
        <td>classname</td>
        <td>
          The name of your class that
          implements <code class="code">org.apache.tools.ant.types.selectors.FileSelector</code>.
        </td>
        <td>Yes</td>
      </tr>
      <tr>
        <td>classpath</td>
        <td>
          The classpath to use in order to load the custom selector class. If
          neither <var>classpath</var> nor <var>classpathref</var> are specified, the class will be
          loaded from the classpath that Ant uses.
        </td>
        <td>No</td>
      </tr>
      <tr>
        <td>classpathref</td>
        <td>
          A reference to a classpath previously defined. If neither <var>classpathref</var>
          nor <var>classpath</var> above are specified, the class will be loaded from the classpath
          that Ant uses.
        </td>
        <td>No</td>
      </tr>
    </table>

    <p>
      Here is how you use <code>&lt;custom&gt;</code> to use your class as a selector:
    </p>
    <pre>
&lt;fileset dir="${mydir}" includes="**/*"&gt;
    &lt;custom classname="com.mydomain.MySelector"&gt;
        &lt;param name="myattribute" value="myvalue"/&gt;
    &lt;/custom&gt;
&lt;/fileset&gt;</pre>
    <p>The core selectors that can also be used as custom selectors are</p>

    <ul>
      <li><a href="selectors.html#containsselect">Contains Selector</a> with
        classname <code class="code">org.apache.tools.ant.types.selectors.ContainsSelector</code></li>
      <li><a href="selectors.html#dateselect">Date Selector</a> with
        classname <code class="code">org.apache.tools.ant.types.selectors.DateSelector</code></li>
      <li><a href="selectors.html#depthselect">Depth Selector</a> with
        classname <code class="code">org.apache.tools.ant.types.selectors.DepthSelector</code></li>
      <li><a href="selectors.html#filenameselect">Filename Selector</a> with
        classname <code class="code">org.apache.tools.ant.types.selectors.FilenameSelector</code></li>
      <li><a href="selectors.html#sizeselect">Size Selector</a> with
        classname <code class="code">org.apache.tools.ant.types.selectors.SizeSelector</code></li>
    </ul>

    <p>
      Here is the example from the Depth Selector section rewritten to use the selector
      through <code>&lt;custom&gt;</code>.
    </p>
    <pre>
&lt;fileset dir="${doc.path}" includes="**/*"&gt;
    &lt;custom classname="org.apache.tools.ant.types.selectors.DepthSelector"&gt;
        &lt;param name="max" value="1"/&gt;
    &lt;/custom&gt;
&lt;/fileset&gt;</pre>
    <p>Selects all files in the base directory and one directory below that.</p>

    <h3 id="filterreaders">Custom Filter Readers</h3>
    <p>
      Custom filter readers selectors are datatypes that
      implement <code class="code">org.apache.tools.ant.types.filters.ChainableReader</code>.
    </p>
    <p>
      There is only one method required, <code class="code">Reader chain(Reader reader)</code>.
      This returns a reader that filters input from the specified reader.
    </p>
    <p>
      For example a filterreader that removes every second character could be:
    </p>
    <pre>
public class RemoveOddCharacters implements ChainableReader {
    public Reader chain(Reader reader) {
        return new BaseFilterReader(reader) {
            int count = 0;
            public int read() throws IOException {
                while (true) {
                    int c = in.read();
                    if (c == -1) {
                        return c;
                    }
                    count++;
                    if ((count % 2) == 1) {
                        return c;
                    }
                }
            }
        }
    }
}</pre>
    <p>
      For line oriented filters it may be easier to
      extend <code class="code">ChainableFilterReader</code> an inner class
      of <code class="code">org.apache.tools.ant.filters.TokenFilter</code>.
    </p>
    <p>
      For example a filter that appends the line number could be
    </p>
    <pre>
public class AddLineNumber extends ChainableReaderFilter {
    private void lineNumber = 0;
    public String filter(String string) {
        lineNumber++;
        return "" + lineNumber + "\t" + string;
    }
}</pre>

  </body>
</html>