aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/manual/tutorial-tasks-filesets-properties.html
blob: 3a36e7ef0b31643d2bb3957952ad0552812157c6 (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
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
<!--
   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

       http://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>
<head>
  <link rel="stylesheet" type="text/css" href="stylesheets/style.css">
  <title>Tutorial: Tasks using Properties, Filesets &amp; Paths</title>
</head>
<body>
<h1>Tutorial: Tasks using Properties, Filesets &amp; Paths</h1>

<p>After reading the tutorial about <a href="tutorial-writing-tasks.html">writing
tasks [1]</a> this tutorial explains how to get and set properties and how to use
nested filesets and paths. Finally it explains how to contribute tasks to Apache Ant.</p>

<h2>Content</h2>
<p><ul>
<li><a href="#goal">The goal</a></li>
<li><a href="#buildenvironment">Build environment</a></li>
<li><a href="#propertyaccess">Property access</a></li>
<li><a href="#filesets">Using filesets</a></li>
<li><a href="#path">Using nested paths</a></li>
<li><a href="#returning-list">Returning a list</a></li>
<li><a href="#documentation">Documentation</a></li>
<li><a href="#contribute">Contribute the new task</a></li>
<li><a href="#resources">Resources</a></li>
</ul></p>


<h2><a name="goal">The goal</a></h2>
<p>The goal is to write a task, which searchs in a path for a file and saves the
location of that file in a property.</p>


<h2><a name="buildenvironment">Build environment</a></h2>
<p>We can use the buildfile from the other tutorial and modify it a little bit.
That's the advantage of using properties - we can reuse nearly the whole script. :-)</p>
<pre class="code">
&lt;?xml version="1.0" encoding="ISO-8859-1"?&gt;
&lt;project name="<b>FindTask</b>" basedir="." default="test"&gt;
    ...
    &lt;target name="use.init" description="Taskdef's the <b>Find</b>-Task" depends="jar"&gt;
        &lt;taskdef name="<b>find</b>" classname="<b>Find</b>" classpath="${ant.project.name}.jar"/&gt;
    &lt;/target&gt;

    <b>&lt;!-- the other use.* targets are deleted --&gt;</b>
    ...
&lt;/project&gt;
</pre>

<p>The buildfile is in the archive <a href="tutorial-tasks-filesets-properties.zip">
tutorial-tasks-filesets-properties.zip [2]</a> in <tt>/build.xml.01-propertyaccess</tt>
(future version saved as *.02..., final version as build.xml; same for sources).</p>


<h2><a name="propertyaccess">Property access</a></h2>
<p>Our first step is to set a property to a value and print the value of that property.
So our scenario would be
<pre class="code">
    &lt;find property="test" value="test-value"/&gt;
    &lt;find print="test"/&gt;
</pre>
ok, can be rewritten with the core tasks
<pre class="code">
    &lt;property name="test" value="test-value"/&gt;
    &lt;echo message="${test}"/&gt;
</pre>
but I have to start on known ground :-)</p>
<p>So what to do? Handling three attributes (property, value, print) and an execute method.
Because this is only an introduction example I don't do much checking:

<pre class="code">
import org.apache.tools.ant.BuildException;

public class Find extends Task {

    private String property;
    private String value;
    private String print;

    public void setProperty(String property) {
        this.property = property;
    }

    // setter for value and print

    public void execute() {
        if (print != null) {
            String propValue = <b>getProject().getProperty(print)</b>;
            log(propValue);
        } else {
            if (property == null) throw new BuildException("property not set");
            if (value    == null) throw new BuildException("value not set");
            <b>getProject().setNewProperty(property, value)</b>;
        }
    }
}
</pre>

As said in the other tutorial, the property access is done via Project instance.
We get this instance via the public <tt>getProject()</tt> method which we inherit from
<tt>Task</tt> (more precise from ProjectComponent). Reading a property is done via
<tt>getProperty(<i>propertyname</i>)</tt> (very simple, isn't it?). This property returns
the value as String or <i>null</i> if not set.<br>
Setting a property is ... not really difficult, but there is more than one setter. You can
use the <tt>setProperty()</tt> method which will do the job like expected. But there is
a golden rule in Ant: <i>properties are immutable</i>. And this method sets the property
to the specified value - whether it has a value before that or not. So we use another
way. <tt>setNewProperty()</tt> sets the property only if there is no property with that
name. Otherwise a message is logged.</p>

<p><i>(by the way: a short word to ants "namespaces" (don't
be confused with xml namespaces:
an <code>&lt;antcall&gt;</code> creates a new space for property names. All properties from the caller
are passed to the callee, but the callee can set its own properties without notice by the
caller.)</i></p>

<p>There are some other setter, too (but I haven't used them, so I can't say something
to them, sorry :-)</p>

<p>After putting our two line example from above into a target names <tt>use.simple</tt>
we can call that from our testcase:

<pre class="code">
import org.junit.Rule;
import org.junit.Test;
import org.junit.Before;
import org.junit.Assert;
import org.apache.tools.ant.BuildFileRule;


public class FindTest {

    @Rule
    public final BuildFileRule buildRule = new BuildFileRule();


    @Before
    public void setUp() {
        configureProject("build.xml");
    }

    @Test
    public void testSimple() {
        buildRule.executeTarget("useSimgle");
        <b>Assert.assertEquals("test-value", buildRule.getLog());</b>
    }
}
</pre>

and all works fine.</p>



<h2><a name="filesets">Using filesets</a></h2>
<p>Ant provides a common way of bundling files: the fileset. Because you are reading
this tutorial I think you know them and I don't have to spend more explanations about
their usage in buildfiles. Our goal is to search a file in path. And on this step the
path is simply a fileset (or more precise: a collection of filesets). So our usage
would be
<pre class="code">
    &lt;find file="ant.jar" location="location.ant-jar"&gt;
        &lt;fileset dir="${ant.home}" includes="**/*.jar"/&gt;
    &lt;/find&gt;
</pre>
</p>

<p>What do we need? A task with two attributes (file, location) and nested
filesets. Because we had attribute handling already explained in the example above and the
handling of nested elements is described in the other tutorial the code should be very easy:
<pre class="code">
public class Find extends Task {

    private String file;
    private String location;
    private Vector filesets = new Vector();

    public void setFile(String file) {
        this.file = file;
    }

    public void setLocation(String location) {
        this.location = location;
    }

    public void addFileset(FileSet fileset) {
        filesets.add(fileset);
    }

    public void execute() {
    }
}
</pre>
Ok - that task wouldn't do very much, but we can use it in the described manner without
failure. On next step we have to implement the execute method. And before that we will
implement the appropriate testcases (TDD - test driven development).</p>

<p>In the other tutorial we have reused the already written targets of our buildfile.
Now we will configure most of the testcases via java code (sometimes it's much easier
to write a target than doing it via java coding). What can be tested?<ul>
<li>not valid configured task (missing file, missing location, missing fileset)</li>
<li>don't find a present file</li>
<li>behaviour if file can't be found</li>
</ul>
Maybe you find some more testcases. But this is enough for now.<br>
For each of these points we create a <tt>testXX</tt> method.</p>

<pre class="code">
public class FindTest {

    @Rule
    public final BuildFileRule buildRule = new BuildFileRule();

    ... // constructor, setUp as above

    @Test
    public void testMissingFile() {
        <b>Find find = new Find();</b>
        try {
            <b>find.execute();</b>
            fail("No 'no-file'-exception thrown.");
        } catch (Exception e) {
            // exception expected
            String expected = "file not set";
            assertEquals("Wrong exception message.", expected, e.getMessage());
        }
    }

    @Test
    public void testMissingLocation() {
        Find find = new Find();
        <b>find.setFile("ant.jar");</b>
        try {
            find.execute();
            fail("No 'no-location'-exception thrown.");
        } catch (Exception e) {
            ... // similar to testMissingFile()
        }
    }

    @Test
    public void testMissingFileset() {
        Find find = new Find();
        find.setFile("ant.jar");
        find.setLocation("location.ant-jar");
        try {
            find.execute();
            fail("No 'no-fileset'-exception thrown.");
        } catch (Exception e) {
            ... // similar to testMissingFile()
        }
    }

    @Test
    public void testFileNotPresent() {
        buildRule.executeTarget("testFileNotPresent");
        String result = buildRule.getProject().getProperty("location.ant-jar");
        assertNull("Property set to wrong value.", result);
    }

    @Test
    public void testFilePresent() {
        buildRule.executeTarget("testFilePresent");
        String result = buildRule.getProject().getProperty("location.ant-jar");
        assertNotNull("Property not set.", result);
        assertTrue("Wrong file found.", result.endsWith("ant.jar"));
    }
}
</pre>

<p>If we run this test class all test cases (except <i>testFileNotPresent</i>) fail. Now we
can implement our task, so that these test cases will pass.</p>

<pre class="code">
    protected void validate() {
        if (file==null) throw new BuildException("file not set");
        if (location==null) throw new BuildException("location not set");
        if (filesets.size()&lt;1) throw new BuildException("fileset not set");
    }

    public void execute() {
        validate();                                                             // 1
        String foundLocation = null;
        for(Iterator itFSets = filesets.iterator(); itFSets.hasNext(); ) {      // 2
            FileSet fs = (FileSet)itFSets.next();
            DirectoryScanner ds = fs.getDirectoryScanner(getProject());         // 3
            String[] includedFiles = ds.getIncludedFiles();
            for(int i=0; i&lt;includedFiles.length; i++) {
                String filename = includedFiles[i].replace('\\','/');           // 4
                filename = filename.substring(filename.lastIndexOf("/")+1);
                if (foundLocation==null &amp;&amp; file.equals(filename)) {
                    File base  = ds.getBasedir();                               // 5
                    File found = new File(base, includedFiles[i]);
                    foundLocation = found.getAbsolutePath();
                }
            }
        }
        if (foundLocation!=null)                                                // 6
            getProject().setNewProperty(location, foundLocation);
    }
</pre>

<p>On <b>//1</b> we check the prerequisites for our task. Doing that in a <tt>validate</tt>-method
is a common way, because we separate the prerequisites from the real work. On <b>//2</b> we iterate
over all nested filesets. If we don't want to handle multiple filesets, the <tt>addFileset()</tt>
method has to reject the further calls. We can get the result of a fileset via its DirectoryScanner
like done in <b>//3</b>. After that we create a platform independent String representation of
the file path (<b>//4</b>, can be done in other ways of course). We have to do the <tt>replace()</tt>,
because we work with a simple string comparison. Ant itself is platform independent and can
therefore run on filesystems with slash (/, e.g. Linux) or backslash (\, e.g. Windows) as
path separator. Therefore we have to unify that. If we found our file we create an absolute
path representation on <b>//5</b>, so that we can use that information without knowing the basedir.
(This is very important on use with multiple filesets, because they can have different basedirs
and the return value of the directory scanner is relative to its basedir.) Finally we store the
location of the file as property, if we had found one (<b>//6</b>).</p>

<p>Ok, much more easier in this simple case would be to add the <i>file</i> as additional
<i>include</i> element to all filesets. But I wanted to show how to handle complex situations
without being complex :-)</p>

<p>The test case uses the ant property <i>ant.home</i> as reference. This property is set by the
<tt>Launcher</tt> class which starts ant. We can use that property in our buildfiles as a
<a href="properties.html#built-in-props">build-in property [3]</a>. But if we create a new ant
environment we have to set that value for our own. And we use the <code>&lt;junit&gt;</code> task in fork-mode.
Therefore we have do modify our buildfile:
<pre class="code">
    &lt;target name="junit" description="Runs the unit tests" depends="jar"&gt;
        &lt;delete dir="${junit.out.dir.xml}"/&gt;
        &lt;mkdir  dir="${junit.out.dir.xml}"/&gt;
        &lt;junit printsummary="yes" haltonfailure="no"&gt;
            &lt;classpath refid="classpath.test"/&gt;
            <b>&lt;sysproperty key="ant.home" value="${ant.home}"/&gt;</b>
            &lt;formatter type="xml"/&gt;
            &lt;batchtest fork="yes" todir="${junit.out.dir.xml}"&gt;
                &lt;fileset dir="${src.dir}" includes="**/*Test.java"/&gt;
            &lt;/batchtest&gt;
        &lt;/junit&gt;
    &lt;/target&gt;
</pre>


<h2><a name="path">Using nested paths</a></h2>
<p>A task providing support for filesets is a very comfortable one. But there is another
possibility of bundling files: the <code>&lt;path&gt;</code>. Fileset are easy if the files are all under
a common base directory. But if this is not the case you have a problem. Another disadvantage
is its speed: if you have only a few files in a huge directory structure, why not use a
<code>&lt;filelist&gt;</code> instead? <code>&lt;path&gt;</code>s combines these datatypes in that way that a path contains
other paths, filesets, dirsets and filelists. This is why <a href="http://ant-contrib.sourceforge.net/">
Ant-Contribs [4]</a> <code>&lt;foreach&gt;</code> task is modified to support paths instead of filesets. So we want that,
too.</p>

<p>Changing from fileset to path support is very easy:</p>
<pre class="code">
<i><b>Change java code from:</b></i>
    private Vector filesets = new Vector();
    public void addFileset(FileSet fileset) {
        filesets.add(fileset);
    }
<i><b>to:</b></i>
    private Vector paths = new Vector();                      *1
    public void add<b>Path</b>(<b>Path</b> path) {                          *2
        paths.add(path);
    }
<i><b>and build file from:</b></i>
    &lt;find file="ant.jar" location="location.ant-jar"&gt;
        &lt;fileset dir="${ant.home}" includes="**/*.jar"/&gt;
    &lt;/find&gt;
<i><b>to:</b></i>
    &lt;find file="ant.jar" location="location.ant-jar"&gt;
        <b>&lt;path&gt;</b>                                                *3
            &lt;fileset dir="${ant.home}" includes="**/*.jar"/&gt;
        &lt;/path&gt;
    &lt;/find&gt;
</pre>
<p>On <b>*1</b> we rename only the vector. It�s just for better reading the source. On <b>*2</b>
we have to provide the right method: an add<i>Name</i>(<i>Type</i> t). Therefore replace the
fileset with path here. Finally we have to modify our buildfile on <b>*3</b> because our task
doesn�t support nested filesets any longer. So we wrap the fileset inside a path.</p>

<p>And now we modify the testcase. Oh, not very much to do :-) Renaming the <tt>testMissingFileset()</tt>
(not really a <i>must-be</i> but better it�s named like the think it does) and update the
<i>expected</i>-String in that method (now a <tt>path not set</tt> message is expected). The more complex
test cases base on the buildscript. So the targets <tt>testFileNotPresent</tt> and <tt>testFilePresent</tt> have to be
modified in the manner described above.</p>

<p>The test are finished. Now we have to adapt the task implementation. The easiest modification is
in the <tt>validate()</tt> method where we change le last line to <tt>if (paths.size()&lt;1) throw new
BuildException("path not set");</tt>. In the <tt>execute()</tt> method we have a little more work.
... mmmh ... in reality it's lesser work, because the Path class does the whole DirectoryScanner-handling
and creating-absolute-paths stuff for us. So the execute method is just:</p>

<pre class="code">
    public void execute() {
        validate();
        String foundLocation = null;
        for(Iterator itPaths = paths.iterator(); itPaths.hasNext(); ) {
            Path path = (<b>Path</b>)itPaths.next();                                // 1
            String[] includedFiles = <b>path.list()</b>;                            // 2
            for(int i=0; i&lt;includedFiles.length; i++) {
                String filename = includedFiles[i].replace('\\','/');
                filename = filename.substring(filename.lastIndexOf("/")+1);
                if (foundLocation==null &amp;&amp; file.equals(filename)) {
                    <b>foundLocation = includedFiles[i];</b>                        // 3
                }
            }
        }
        if (foundLocation!=null)
            getProject().setNewProperty(location, foundLocation);
    }
</pre>

<p>Of course we have to do the typecase to Path on <b>//1</b>. On <b>//2</b> and <b>//3</b>
we see that the Path class does the work for us: no DirectoryScanner (was at 2) and no
creating of the absolute path (was at 3).</p>



<h2><a name="returning-list">Returning a list</a></h2>
<p>So far so good. But could a file be on more than one place in the path? - Of course.<br>
And would it be good to get all of them? - It depends on ...<p>

<p>In this section we will extend that task to support returning a list of all files.
Lists as property values are not supported by Ant natively. So we have to see how other
tasks use lists. The most famous task using lists is Ant-Contribs <code>&lt;foreach&gt;</code>. All list
elements are concatenated and separated with a customizable separator (default ',').</p>

<p>So we do the following:</p>

<pre class="code">
    &lt;find ... <b>delimiter=""</b>/&gt; ... &lt;/find&gt;
</pre>

<p>If the delimiter is set we will return all found files as list with that delimiter.</p>

<p>Therefore we have to<ul>
<li>provide a new attribute</li>
<li>collect more than the first file</li>
<li>delete duplicates</li>
<li>create the list if necessary</li>
<li>return that list</li>
</ul></p>

<p>So we add as testcase:</p>
<pre class="code">
<b><i>in the buildfile:</i></b>
    &lt;target name="test.init"&gt;
        &lt;mkdir dir="test1/dir11/dir111"/&gt;                             *1
        &lt;mkdir dir="test1/dir11/dir112"/&gt;
        ...
        &lt;touch file="test1/dir11/dir111/test"/&gt;
        &lt;touch file="test1/dir11/dir111/not"/&gt;
        ...
        &lt;touch file="test1/dir13/dir131/not2"/&gt;
        &lt;touch file="test1/dir13/dir132/test"/&gt;
        &lt;touch file="test1/dir13/dir132/not"/&gt;
        &lt;touch file="test1/dir13/dir132/not2"/&gt;
        &lt;mkdir dir="test2"/&gt;
        &lt;copy todir="test2"&gt;                                          *2
            &lt;fileset dir="test1"/&gt;
        &lt;/copy&gt;
    &lt;/target&gt;

    &lt;target name="testMultipleFiles" depends="use.init,<b>test.init</b>"&gt;    *3
        &lt;find file="test" location="location.test" <b>delimiter=";"</b>&gt;
            &lt;path&gt;
                &lt;fileset dir="test1"/&gt;
                &lt;fileset dir="test2"/&gt;
            &lt;/path&gt;
        &lt;/find&gt;
        &lt;delete&gt;                                                      *4
            &lt;fileset dir="test1"/&gt;
            &lt;fileset dir="test2"/&gt;
        &lt;/delete&gt;
    &lt;/target&gt;

<b><i>in the test class:</i></b>
    public void testMultipleFiles() {
        executeTarget("testMultipleFiles");
        String result = getProject().getProperty("location.test");
        assertNotNull("Property not set.", result);
        assertTrue("Only one file found.", result.indexOf(";") &gt; -1);
    }
</pre>

<p>Now we need a directory structure where we CAN find files with the same
name in different directories. Because we can't sure to have one we create
one on <b>*1</b> and <b>*2</b>. And of course we clean up that on <b>*4</b>. The creation
can be done inside our test target or in a separate one, which will be better
for reuse later (<b>*3</b>).

<p>The task implementation is modified as followed:</p>

<pre class="code">
    private Vector foundFiles = new Vector();
    ...
    private String delimiter = null;
    ...
    public void setDelimiter(String delim) {
        delimiter = delim;
    }
    ...
    public void execute() {
        validate();
        // find all files
        for(Iterator itPaths = paths.iterator(); itPaths.hasNext(); ) {
            Path path = (Path)itPaths.next();
            String[] includedFiles = path.list();
            for(int i=0; i&lt;includedFiles.length; i++) {
                String filename = includedFiles[i].replace('\\','/');
                filename = filename.substring(filename.lastIndexOf("/")+1);
                if (file.equals(filename) &amp;&amp; <b>!foundFiles.contains(includedFiles[i]</b>)) {   // 1
                    foundFiles.add(includedFiles[i]);
                }
            }
        }

        // create the return value (list/single)
        String rv = null;
        if (foundFiles.size() &gt; 0) {                                        // 2
            if (delimiter==null) {
                // only the first
                rv = (String)foundFiles.elementAt(0);
            } else {
                // create list
                StringBuffer list = new StringBuffer();
                for(Iterator it=foundFiles.iterator(); it.hasNext(); ) {    // 3
                    list.append(it.next());
                    if (<b>it.hasNext()</b>) list.append(delimiter);               // 4
                }
                rv = list.toString();
            }
        }

        // create the property
        if (rv!=null)
            getProject().setNewProperty(location, rv);
    }
</pre>

<p>The algorithm does: finding all files, creating the return value depending on the users
wish, returning the value as property. On <b>//1</b> we eliminates the duplicates. <b>//2</b>
ensures that we create the return value only if we have found one file. On <b>//3</b> we
iterate over all found files and <b>//4</b> ensures that the last entry has no trailing
delimiter.</p>

<p>Ok, first searching for all files and then returning only the first one ... You can
tune the performance of your own :-)</p>


<h2><a name="documentation">Documentation</a></h2>
<p>A task is useless if the only who is able to code the buildfile is the task developer
(and he only the next few weeks :-). So documentation is also very important. In which
form you do that depends on your favourite. But inside Ant there is a common format and
it has advantages if you use that: all task users know that form, this form is requested if
you decide to contribute your task. So we will doc our task in that form.</p>

<p>If you have a look at the manual page of the <a href="Tasks/java.html">Java task [5]</a>
   you will see that it:<ul>
<li>is plain html</li>
<li>starts with the name</li>
<li>has sections: description, parameters, nested elements, (maybe return codes) and (most
important :-) examples</li>
<li>parameters are listed in a table with columns for attribute name, its description and whether
  it's required (if you add a feature after an Ant release, provide a <tt>since Ant xx</tt>
  statement when it's introduced)</li>
<li>describe the nested elements (since-statement if necessary)</li>
<li>provide one or more useful examples; first code, then description.</li>
</ul>
As a template we have:

<pre class="code">
&lt;html&gt;

&lt;head&gt;
&lt;meta http-equiv="Content-Language" content="en-us"&gt;
&lt;title&gt;<b>Taskname</b> Task&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;

&lt;h2&gt;&lt;a name="<b>taskname</b>"&gt;<b>Taskname</b>&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;p&gt; <b>Describe the task.</b>&lt;/p&gt;

&lt;h3&gt;Parameters&lt;/h3&gt;
&lt;table border="1" cellpadding="2" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td valign="top"&gt;&lt;b&gt;Attribute&lt;/b&gt;&lt;/td&gt;
    &lt;td valign="top"&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;&lt;b&gt;Required&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;

  <b>do this html row for each attribute (including inherited attributes)</b>
  &lt;tr&gt;
    &lt;td valign="top"&gt;classname&lt;/td&gt;
    &lt;td valign="top"&gt;the Java class to execute.&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;Either jar or classname&lt;/td&gt;
  &lt;/tr&gt;

&lt;/table&gt;

&lt;h3&gt;Parameters specified as nested elements&lt;/h3&gt;

<b>Describe each nested element (including inherited)</b>
&lt;h4&gt;<b>your nested element</b>&lt;/h4&gt;
&lt;p&gt;<b>description</b>&lt;/p&gt;
&lt;p&gt;&lt;em&gt;since Ant 1.6&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;
    <b>A code sample; don't forget to escape the &lt; of the tags with &amp;lt;</b>
&lt;/pre&gt;
<b>What should that example do?</b>

&lt;/body&gt;
&lt;/html&gt;
</pre>

<p>Here is an example documentation page for our task:</p>
<pre class="code">
&lt;html&gt;

&lt;head&gt;
&lt;meta http-equiv="Content-Language" content="en-us"&gt;
&lt;title&gt;Find Task&lt;/title&gt;
&lt;/head&gt;

&lt;body&gt;

&lt;h2&gt;&lt;a name="find"&gt;Find&lt;/a&gt;&lt;/h2&gt;
&lt;h3&gt;Description&lt;/h3&gt;
&lt;p&gt;Searchs in a given path for a file and returns the absolute to it as property.
If delimiter is set this task returns all found locations.&lt;/p&gt;

&lt;h3&gt;Parameters&lt;/h3&gt;
&lt;table border="1" cellpadding="2" cellspacing="0"&gt;
  &lt;tr&gt;
    &lt;td valign="top"&gt;&lt;b&gt;Attribute&lt;/b&gt;&lt;/td&gt;
    &lt;td valign="top"&gt;&lt;b&gt;Description&lt;/b&gt;&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;&lt;b&gt;Required&lt;/b&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td valign="top"&gt;file&lt;/td&gt;
    &lt;td valign="top"&gt;The name of the file to search.&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;yes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td valign="top"&gt;location&lt;/td&gt;
    &lt;td valign="top"&gt;The name of the property where to store the location&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;yes&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td valign="top"&gt;delimiter&lt;/td&gt;
    &lt;td valign="top"&gt;A delimiter to use when returning the list&lt;/td&gt;
    &lt;td align="center" valign="top"&gt;only if the list is required&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;

&lt;h3&gt;Parameters specified as nested elements&lt;/h3&gt;

&lt;h4&gt;path&lt;/h4&gt;
&lt;p&gt;The path where to search the file.&lt;/p&gt;

&lt;h3&gt;Examples&lt;/h3&gt;
&lt;pre&gt;
    &lt;find file="ant.jar" location="loc"&gt;
        &lt;path&gt;
            &lt;fileset dir="${ant.home}"/&gt;
        &lt;path&gt;
    &lt;/find&gt;
&lt;/pre&gt;
Searches in Ants home directory for a file &lt;i&gt;ant.jar&lt;/i&gt; and stores its location in
property &lt;i&gt;loc&lt;/i&gt; (should be ANT_HOME/bin/ant.jar).

&lt;pre&gt;
    &lt;find file="ant.jar" location="loc" delimiter=";"&gt;
        &lt;path&gt;
            &lt;fileset dir="C:/"/&gt;
        &lt;path&gt;
    &lt;/find&gt;
    &lt;echo&gt;ant.jar found in: ${loc}&lt;/echo&gt;
&lt;/pre&gt;
Searches in Windows C: drive for all &lt;i&gt;ant.jar&lt;/i&gt; and stores their locations in
property &lt;i&gt;loc&lt;/i&gt; delimited with &lt;i&gt;';'&lt;/i&gt;. (should need a long time :-)
After that it prints out the result (e.g. C:/ant-1.5.4/bin/ant.jar;C:/ant-1.6/bin/ant.jar).

&lt;/body&gt;
&lt;/html&gt;
</pre>


<h2><a name="contribute">Contribute the new task</a></h2>
If we decide to contribute our task, we should do some things:<ul>
<li>is our task welcome? :-) Simply ask on the user list</li>
<li>is the right package used? </li>
<li>does the code conform to the styleguide?</li>
<li>do all tests pass? </li>
<li>does the code compile on JDK 1.2 (and passes all tests there)?</li>
<li>code under Apache license</li>
<li>create a patch file</li>
<li>publishing that patch file</li>
</ul>
The <a href="http://ant.apache.org/ant_task_guidelines.html">Ant Task Guidelines [6]</a> support additional
information on that.</p>

<p>Now we will check the "Checklist before submitting a new task" described in that guideline.
<ul>
<li>Java file begins with Apache license statement. <b><i>must do that</i></b></li>
<li>Task does not depend on GPL or LGPL code. <b><i>ok</i></b></li>
<li>Source code complies with style guidelines <b><i>have to check (checkstyle)</i></b></li>
<li>Code compiles and runs on Java1.2 <b><i>have to try</i></b></li>
<li>Member variables are private, and provide public accessor methods
        if access is actually needed. <b><i>have to check (checkstyle)</i></b></li>
<li><i>Maybe</i> Task has failonerror attribute to control failure behaviour <b><i>hasn't</i></b></li>
<li>New test cases written and succeed <b><i>passed on JDK 1.4, have to try on JDK 1.2</i></b></li>
<li>Documentation page written <b><i>ok</i></b></li>
<li>Example task declarations in the documentation tested. <b><i>ok (used in tests)</i></b></li>
<li>Patch files generated using cvs diff -u <b><i>to do</i></b></li>
<li>patch files include a patch to defaults.properties to register the
tasks <b><i>to do</i></b></li>
<li>patch files include a patch to tasklist.html to link to the new task page <b><i>to do</i></b></li>
<li>Message to dev contains [SUBMIT] and task name in subject <b><i>to do</i></b></li>
<li>Message body contains a rationale for the task <b><i>to do</i></b></li>
<li>Message attachments contain the required files -source, documentation,
test and patches zipped up to escape the HTML filter. <b><i>to do</i></b></li>
</ul>


<h3>Package / Directories</h3>
<p>This task does not depend on any external library. Therefore we can use this as
a core task. This task contains only one class. So we can use the standard package
for core tasks: <tt>org.apache.tools.ant.taskdefs</tt>. Implementations are in the
directory <tt>src/main</tt>, tests in <tt>src/testcases</tt> and buildfiles for
tests in <tt>src/etc/testcases</tt>.</p>

<p>Now we integrate our work into Ants distribution. So first we do an update of our
cvs tree. If not done yet, you have to checkout the ant module from Apaches cvs server
as described in <a href="http://ant.apache.org/cvs.html">Access the Source Tree (AnonCVS)
[7]</a> (password is <i>anoncvs</i>):<pre class="output">
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login                 //1
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic checkout ant          //2
</pre>
If you have a local copy of Ants sources just do an update
<pre class="output">
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic login
cd ant                                                                       //3
cvs -d :pserver:anoncvs@cvs.apache.org:/home/cvspublic update                //4
</pre></p>

<p>We use the <i>-d</i> flag on <b>//1</b> to specify the cvs directory. You can
specify the environment variable CVSROOT with that value and after that you haven�t
to use that flag any more. On <b>//2</b> we get the whole cvs tree of ant. (Sorry,
but that uses a lot of time ... 10 up to 30 minutes are not unusual ... but this has
to be done only once :-). A cvs update doesn't use a modulename but you have to be
inside the directory. Therefore we go into that on <b>//3</b> and do the update
on <b>//4</b>.</p>

<p>Now we will build our Ant distribution and do a test. So we can see if there
are any tests failing on our machine. (We can ignore these failing tests on later
steps; windows syntax used here- translate to xNIX if needed):
<pre class="output">
ANTHOME&gt; build                                                    // 1
ANTHOME&gt; set ANT_HOME=%CD%\dist                                   // 2
ANTHOME&gt; ant test -Dtest.haltonfailure=false                      // 3
</pre>

First we have to build our Ant distribution (<b>//1</b>). On <b>//2</b> we set the ANT_HOME
environment variable to the directory where the new created distribution is stored
(%CD% is expanded to the current directory on Windows 2000 and XP, on 9x and NT
write it out). On <b>//3</b> we let Ant do all the tests (which enforced a compile
of all tests) without stopping on first failure.</p>

<p>Next we apply our work onto Ants sources. Because we haven't modified any, this is
a relative simple step. <i>(Because I have a local copy of Ant and usually contribute my
work, I work on the local copy just from the beginning. The advantage: this step isn't
necessary and saves a lot of work if you modify existing source :-)</i>.

<ul>
<li>move the Find.java to ANTHOME/src/main/org/apache/tools/ant/taskdefs/Find.java </li>
<li>move the FindTest.java to ANTHOME/src/testcases/org/apache/tools/ant/taskdefs/FindTest.java </li>
<li>move the build.xml to ANTHOME/src/etc/testcases/taskdefs/<b>find.xml</b> (!!! renamed !!!)</li>
<li>add a <tt>package org.apache.tools.ant.taskdefs;</tt> at the beginning of the two java files </li>
<li>delete all stuff from find.xml keeping the targets "testFileNotPresent", "testFilePresent",
    "test.init" and "testMultipleFiles" </li>
<li>delete the dependency to "use.init" in the find.xml </li>
<li>in FindTest.java change the line <tt>configureProject("build.xml");</tt> to
    <tt>configureProject("src/etc/testcases/taskdefs/find.xml");</tt> </li>
<li>move the find.html to ANTHOME/docs/manual/Tasks/find.html </li>
<li>add a <tt>&lt;a href="Tasks/find.html"&gt;Find&lt;/a&gt;&lt;br&gt;</tt>
    in the ANTHOME/docs/manual/tasklist.html </li>
</ul>

Now our modifications are done and we will retest it:
<pre class="output">
ANTHOME&gt; build
ANTHOME&gt; ant run-single-test                                      // 1
             -Dtestcase=org.apache.tools.ant.taskdefs.FindTest    // 2
             -Dtest.haltonfailure=false
</pre>
Because we only want to test our new class, we use the target for single tests, specify
the test to use and configure not to halt on the first failure - we want to see all
failures of our own test (<b>//1 + 2</b>).</p>

<p>And ... oh, all tests fail: <i>Ant could not find the task or a class this task relies upon.</i></p>

<p>Ok: in the earlier steps we told Ant to use the Find class for the <code>&lt;find&gt;</code> task (remember the
<code>&lt;taskdef&gt;</code> statement in the "use.init" target). But now we want to introduce that task as
a core task. And nobody wants to taskdef the javac, echo, ... So what to do? The answer is the
src/main/.../taskdefs/default.properties. Here is the mapping between taskname and implementing
class done. So we add a <tt>find=org.apache.tools.ant.taskdefs.Find</tt> as the last core
task (just before the <tt># optional tasks</tt> line). Now a second try:
<pre class="output">
ANTHOME&gt; build                                                    // 1
ANTHOME&gt; ant run-single-test
             -Dtestcase=org.apache.tools.ant.taskdefs.FindTest
             -Dtest.haltonfailure=false
</pre>
We have to rebuild (<b>//1</b>) Ant because the test look in the %ANT_HOME%\lib\ant.jar
(more precise: on the classpath) for the properties file. And we have only modified it in the
source path. So we have to rebuild that jar. But now all tests pass and we check whether our class
breaks some other tests.
<pre class="output">
ANTHOME&gt; ant test -Dtest.haltonfailure=false
</pre>
Because there are a lot of tests this step requires a little bit of time. So use the <i>run-single-test</i>
during development and do the <i>test</i> only at the end (maybe sometimes during development too).
We use the <i>-Dtest.haltonfailure=false</i> here because there could be other tests fail and we have
to look into them.</p>

<p>This test run should show us two things: our test will run and the number of failing tests
is the same as directly after the cvs update (without our modifications).</p>



<h3>Apache license statement</h3>
<p>Simply copy the license text from one the other source from the Ant source tree.</p>


<h3>Test on JDK 1.2</h3>
<p>Until version 1.5 Ant must be able to run on a JDK 1.1. With version 1.6 this is not a
requisite any more. But JDK 1.2 is a must-to-work-with. So we have to test that. You can download older
JDKs from <a href="http://www.oracle.com/technetwork/java/archive-139210.html">Oracle [8]</a>.</p>

<p>Clean the ANT_HOME variable, delete the <i>build, bootstrap</i> and <i>dist</i> directory
and point JAVA_HOME to the JDK 1.2 home directory. Then do the <tt>build</tt>, set ANT_HOME
and run <tt>ant test</tt> (like above).</p>

<p>Our test should pass.</p>



<h3>Checkstyle</h3>
<p>There are many things we have to ensure. Indentation with 4 spaces, blanks here and there, ...
(all described in the <a href="http://ant.apache.org/ant_task_guidelines.html">Ant Task Guidelines [6]</a> which
includes the <a href="http://www.oracle.com/technetwork/java/codeconvtoc-136057.html">Sun code style
[9]</a>). Because there are so many things we would be happy to have a tool for do the checks.
There is one: checkstyle. Checkstyle is available at <a href="http://checkstyle.sourceforge.net/">
Sourceforge [10]</a> and Ant provides with the <tt>check.xml</tt> a buildfile which will do the job
for us.</p>

<p>Download it and put the checkstyle-*-all.jar into your %USERPROFILE%\.ant\lib directory.
All jar's stored there are available to Ant so you haven't to add it to you %ANT_HOME%\lib
directory (this feature was added with Ant 1.6).</p>

<p>So we will run the tests with
<pre class="output">
ANTHOME&gt; ant -f check.xml checkstyle htmlreport
</pre>
I prefer the HTML report because there are lots of messages and we can navigate faster.
Open the ANTHOME/build/reports/checkstyle/html/index.html and navigate to the Find.java. Now we
see that there are some errors: missing whitespaces, unused imports, missing javadocs. So we have
to do that.</p>

<p>Hint: start at the <b>buttom</b> of the file so the line numbers in the report will keep
up to date and you will find the next error place much more easier without redoing the checkstyle.</p>

<p>After cleaning up the code according to the messages we delete the reports directory and
do a second checkstyle run. Now our task isn't listed. That's fine :-)</p>



<!--
  Couldnt create the diff that way for myself, but that should be documented.
  But on the other hand this tutorial should not be forgotten any longer so I
  comment that out.  JHM
<h3>Creating the diff</h3>
<p>Creating a diff for Ant is very easy: just start <tt>ant -f patch.xml</tt> and all is done
automatically. This step requires a cvs executable in your path and internet access (more precise:
cvs access). As a result we get a file <i> TODO </i>.</p>
-->


<h3>Publish the task</h3>
<p>Finally we publish that archive. As described in the <a href="http://ant.apache.org/ant_task_guidelines.html">
Ant Task Guidelines [7]</a> we can post it on the developer mailinglist or we create a BugZilla
entry. For both we need some information:</p>

<table border="1">
<tr>
  <th>subject</th>
  <td><i>short description</i></td>
  <td>Task for finding files in a path</td>
</tr>
<tr>
  <th>body</th>
  <td><i>more details about the path</i></td>
  <td>This new task looks inside a nested <code>&lt;path/&gt;</code> for occurrences of a file and stores
      all locations as a property. See the included manual for details.</td>
</tr>
<tr>
  <th>attachments</th>
  <td><i>all files needed to apply the path</i></td>
  <td>Archive containing a patch with the new and modified resources</td>
</tr>
</table>

<p>Sending an email with these information is very easy and I think I haven't to show that.
The other way - BugZilla - is slightly more difficult. But it has the advantage that entries
will not be forgotten (once per week a report is generated). So I will show this way.</p>

<p>You must have a BugZilla account for that. So open the <a href="http://issues.apache.org/bugzilla/">
BugZilla Main Page [11]</a> and follow the link
<a href="http://issues.apache.org/bugzilla/createaccount.cgi">Open a new Bugzilla account [12]</a>
and the steps described there if you haven't one.</p>

<ol>
<li>From the BugZilla main page choose <a href="http://issues.apache.org/bugzilla/enter_bug.cgi">Enter
    a new bug report [13]</a></li>
<li>Choose "Ant" as product </li>
<li>Version is the last "Alpha (nightly)" (at this time 1.7)</li>
<li>Component is "Core tasks"</li>
<li>Platform and Severity are ok with "Other" and "Normal"</li>
<li>Initial State is ok with "New"</li>
<li>Same with the empty "Assigned to"</li>
<li>It is not required to add yourself as CC, because you are the reporter and therefore will be
    informed on changes</li>
<li>URL: no url required</li>
<li>Summary: add the <i>subject</i> from the table</li>
<li>Description: add the <i>body</i> from the table</li>
<li>Then press "Commit"</li>
<li>After redirecting to the new created bug entry click "Create a New Attachment"</li>
<li>Enter the path to your local path file into "File" or choose it via the "File"'s
    button.</li>
<li>Enter a short description into "Description", so that you could guess, what the
    path file includes. Here we could add "Initial Patch".</li>
<li>The "Content Type" is "auto-detect". You could use the "patch" type, if you only
    provide a single path file, but we want do upload more that one, included in our
    patch.zip.</li>
<li>Then press "Commit"</li>
</ol>
Now the new task is uploaded into the bug database.


<h2><a name="resources">Resources</a></h2>
&nbsp;&nbsp;[1] <a href="tutorial-writing-tasks.html">tutorial-writing-tasks.html</a><br>
&nbsp;&nbsp;[2] <a href="tutorial-tasks-filesets-properties.zip">tutorial-tasks-filesets-properties.zip</a><br>
&nbsp;&nbsp;[3] <a href="properties.html#built-in-props">properties.html#built-in-props</a><br>
&nbsp;&nbsp;[4] <a href="http://ant-contrib.sourceforge.net/">http://ant-contrib.sourceforge.net/</a><br>
&nbsp;&nbsp;[5] <a href="Tasks/java.html">Tasks/java.html</a><br>
&nbsp;&nbsp;[6] <a href="http://ant.apache.org/ant_task_guidelines.html">http://ant.apache.org/ant_task_guidelines.html</a><br>
&nbsp;&nbsp;[7] <a href="http://ant.apache.org/cvs.html">http://ant.apache.org/cvs.html</a><br>
&nbsp;&nbsp;[8] <a href="http://www.oracle.com/technetwork/java/archive-139210.html">http://www.oracle.com/technetwork/java/archive-139210.html</a><br>
&nbsp;&nbsp;[9] <a href="http://www.oracle.com/technetwork/java/codeconvtoc-136057.html">http://www.oracle.com/technetwork/java/codeconvtoc-136057.html</a><br>
&nbsp;&nbsp;[10] <a href="http://checkstyle.sourceforge.net/">http://checkstyle.sourceforge.net/</a><br>
&nbsp;&nbsp;[11] <a href="http://issues.apache.org/bugzilla/">http://issues.apache.org/bugzilla/</a><br>
&nbsp;&nbsp;[12] <a href="http://issues.apache.org/bugzilla/createaccount.cgi">http://issues.apache.org/bugzilla/createaccount.cgi</a><br>
&nbsp;&nbsp;[13] <a href="http://issues.apache.org/bugzilla/enter_bug.cgi">http://issues.apache.org/bugzilla/enter_bug.cgi</a><br>


<!--
  TODO:
  - how to create a path (path.xml / command line)
-->





</body>
</html>